///|
priv struct ProjectTarget {
project_dir : String
mode : ProjectMode
}
///|
priv struct ProjectSettings {
license : String
host : String
preferred_target : String
}
///|
priv struct ProjectFiles {
moon_mod : String
moon_pkg : String
gitignore : String
gitattributes : String
}
///|
priv struct ProjectFile {
filename : String
content : String
}
///|
async fn validate_project_target(
config : CliConfig,
) -> ProjectTarget raise CliError {
let project_dir = absolute_path(config.location.path)
let target : ProjectTarget = { project_dir, mode: config.location.mode }
match target.mode {
Create => ensure_new_project_target(target.project_dir)
Init => ensure_existing_dir(target.project_dir)
}
target
}
///|
async fn ensure_new_project_target(path : String) -> Unit raise CliError {
guard !path_exists(path) else {
raise CliError(
"path already exists: \{path}; use --init to initialize an existing directory",
)
}
}
///|
fn module_name_from_project_dir(project_dir : String) -> String {
let module_name = @path.Path::basename(project_dir).to_owned()
guard !module_name.is_empty() else { "moon-new-mini" }
module_name
}
///|
async fn bootstrap_project(
target : ProjectTarget,
config : CliConfig,
username : String,
preferred : PreferredConfig,
) -> String raise CliError {
match target.mode {
Create => ensure_dir_recursive(target.project_dir)
Init => ()
}
let files = render_project_files(
target.project_dir,
config.overrides,
username,
preferred,
)
write_project_files(target.project_dir, files)
target.project_dir
}
///|
fn render_project_files(
project_dir : String,
overrides : ProjectOverrides,
username : String,
preferred : PreferredConfig,
) -> ProjectFiles {
let settings = project_settings(overrides, preferred)
let module_name = module_name_from_project_dir(project_dir)
{
moon_mod: moon_mod_content(
username~,
module_name~,
settings.license,
settings.host,
settings.preferred_target,
),
moon_pkg: moon_pkg_content(),
gitignore: gitignore_content(),
gitattributes: gitattributes_content(),
}
}
///|
fn project_settings(
overrides : ProjectOverrides,
preferred : PreferredConfig,
) -> ProjectSettings {
{
license: configured_value(
overrides.license,
preferred.license,
DEFAULT_LICENSE,
),
host: configured_value(overrides.host, preferred.host, DEFAULT_HOST),
preferred_target: configured_value(
overrides.preferred_target,
preferred.preferred_target,
DEFAULT_TARGET,
),
}
}
///|
fn configured_value(
explicit : String?,
preferred : String?,
builtin : String,
) -> String {
explicit.unwrap_or(preferred.unwrap_or(builtin))
}
///|
async fn write_project_files(
project_dir : String,
files : ProjectFiles,
) -> Unit raise CliError {
let entries = files.to_entries()
ensure_project_files_absent(project_dir, entries)
for file in entries {
write_new_string_file(
project_file_path(project_dir, file.filename),
file.content,
)
}
}
///|
async fn ensure_project_files_absent(
project_dir : String,
files : ArrayView[ProjectFile],
) -> Unit raise CliError {
for file in files {
let file_path = project_file_path(project_dir, file.filename)
guard !path_exists(file_path) else {
raise CliError("file already exists: \{file_path}")
}
}
}
///|
fn project_file_path(project_dir : String, filename : String) -> String {
@path.Path::join(project_dir, filename).to_string()
}
///|
fn ProjectFiles::to_entries(self : ProjectFiles) -> Array[ProjectFile] {
[
{ filename: "moon.mod", content: self.moon_mod },
{ filename: "moon.pkg", content: self.moon_pkg },
{ filename: ".gitignore", content: self.gitignore },
{ filename: ".gitattributes", content: self.gitattributes },
]
}