mod build;
mod check;
mod control;
mod data;
mod deps;
mod new;
mod prepare;
mod publish;
mod run;
mod setup;
mod targets;
const USAGE: &str = include_str!("usage.txt");
const DEV: &str = "--dev";
const DEBUG_FLAG: &str = "--debug";
const LIST: &str = "--list";
const MOD: &str = "--mod";
const DRY_RUN: &str = "--dry-run";
const HELP: [&str; 2] = ["-h", "--help"];
const DEBUG_WORD: &str = "debug";
const FLAG: &str = "--";
fn schema(format: Option<&str>) -> Result<(), String> {
let all = || yog_formats::names().collect::<Vec<_>>().join(", ");
let name = format.ok_or_else(|| format!("usage: yog schema <format> — {}", all()))?;
let shape =
yog_formats::shape_of(name).ok_or_else(|| format!("no format `{name}` — {}", all()))?;
println!("{}", yog_data::text::to_string_pretty(&shape.describe()));
Ok(())
}
pub(crate) fn main(args: &[String]) {
let rest = args.get(2..).unwrap_or_default();
let has = |flag: &str| rest.iter().any(|a| a == flag);
let result = match args.get(1).map(String::as_str) {
Some("build") => {
build::build(rest.first().is_some_and(|a| a == DEBUG_WORD), has(DEV)).map(|_| ())
}
Some("check") => build::check(),
Some("schema") => schema(rest.first().map(String::as_str)),
Some("bench") => build::run_benches(rest),
Some("serve") => yog_cli::serve::run(),
Some("designer-lib") => build::designer_lib(),
Some(yog_cli::design::host::COMMAND) => {
yog_cli::design::host::serve();
Ok(())
}
Some("new") => new::new_mod(rest.first().map(String::as_str)),
Some("setup") => setup::setup(),
Some("doctor") => {
setup::doctor_cmd();
Ok(())
}
Some("-V" | "--version" | "version") => {
println!("yog {}", env!("CARGO_PKG_VERSION"));
Ok(())
}
Some("add") => deps::add_dep(dep_name(rest), has(MOD)),
Some("remove") => deps::remove_dep(dep_name(rest), has(MOD)),
Some("run") => run_cmd(rest),
Some("control") => control::control(rest),
Some(data::COMMAND) => data::data(rest),
Some("publish") => publish_cmd(rest),
Some("res") => sub(rest, "check", check::res_check, "usage: yog res check"),
Some("content") => sub(
rest,
"check",
check::content_check,
"usage: yog content check",
),
Some("import") => import(rest),
Some("ui") => match rest.first().map(String::as_str) {
Some("gen") => check::ui_gen(rest.get(1..).unwrap_or_default()),
_ => Err("usage: yog ui gen [file.yui ...] (no files: every src/**/*.yui)".into()),
},
Some("-h" | "--help" | "help") | None => {
print!("{USAGE}");
Ok(())
}
Some(other) => Err(format!("unknown command: {other}")),
};
if let Err(e) = result {
eprintln!("yog: error: {e}");
std::process::exit(1);
}
}
fn import(rest: &[String]) -> Result<(), String> {
let imported = yog_cli::content::import_command(rest)?;
for w in &imported.warnings {
eprintln!("warning: {w}");
}
for f in &imported.written {
eprintln!("==> wrote {f}");
}
eprintln!(
" `yog content check` validates them; register with `yog_api::content!();` in src/lib.rs"
);
Ok(())
}
fn sub(
rest: &[String],
word: &str,
run: fn() -> Result<(), String>,
usage: &str,
) -> Result<(), String> {
match rest.first() {
Some(w) if w == word => run(),
_ => Err(usage.into()),
}
}
fn dep_name(rest: &[String]) -> Option<&str> {
rest.iter().map(String::as_str).find(|a| *a != MOD)
}
fn run_cmd(rest: &[String]) -> Result<(), String> {
let has = |flag: &str| rest.iter().any(|a| a == flag);
if has(LIST) {
return run::list_run_targets();
}
let (debug, dev) = (has(DEBUG_FLAG), has(DEV));
let words: Vec<&str> = rest
.iter()
.map(String::as_str)
.filter(|a| !a.starts_with(FLAG))
.collect();
match words.as_slice() {
[loader, mc] => run::run_target(&[*loader, mc].join(yog_ids::sep::HYPHEN), debug, dev),
[id] => run::run_target(id, debug, dev),
_ => Err(
"usage: yog run <target> | <loader> <mc> [--debug] [--dev]; yog run --list [--json]"
.into(),
),
}
}
fn publish_cmd(rest: &[String]) -> Result<(), String> {
const USAGE: &str = "usage: yog publish exports [--dry-run]";
let flags: Vec<&str> = rest.iter().skip(1).map(String::as_str).collect();
let other = flags.iter().find(|a| **a != DRY_RUN);
match (rest.first().map(String::as_str), other) {
(Some("exports"), Some(o)) if HELP.contains(o) => {
println!(
"{USAGE}\n\nPublishes this mod's exports crate (`#[yog_export]` items) to crates.io; --dry-run only generates and packages it."
);
Ok(())
}
(Some("exports"), Some(o)) => Err(format!("unknown flag {o} — {USAGE}")),
(Some("exports"), None) => publish::publish_exports(!flags.is_empty()),
(Some(h), _) if HELP.contains(&h) => {
println!("{USAGE}");
Ok(())
}
(Some(other), _) => Err(format!("unknown publish subcommand: {other}")),
(None, _) => Err(USAGE.into()),
}
}