1mod cmd;
8mod commands;
9mod man;
10mod mix;
11mod sfxgen;
12mod tts;
13mod workers;
14
15use std::env;
16use std::ffi::OsString;
17use std::io::Write;
18use std::path::Path;
19use std::process;
20
21const VERSION: &str = env!("CARGO_PKG_VERSION");
22
23fn main() {
24 let code = match real_main() {
25 Ok(code) => code,
26 Err(err) => {
27 eprintln!("[ERROR] {err:#}");
28 1
29 }
30 };
31 process::exit(code);
32}
33
34fn real_main() -> anyhow::Result<i32> {
35 let mut argv: Vec<OsString> = env::args_os().collect();
36 let program = argv.remove(0);
37
38 if let Some(alias) = alias_command(&program) {
40 argv.insert(0, OsString::from(alias));
41 }
42
43 let Some(first) = argv.first().map(|a| a.to_string_lossy().into_owned()) else {
47 xil_core::log::init("xil");
48 print!("{}", commands::help_text());
49 return Ok(0);
50 };
51
52 match first.as_str() {
53 "-h" | "--help" | "help" => {
54 xil_core::log::init("xil");
55 print!("{}", commands::help_text());
56 return Ok(0);
57 }
58 "-V" | "--version" | "version" => {
59 xil_core::log::init("xil");
60 println!("{VERSION}");
61 return Ok(0);
62 }
63 "--generate-man" => {
65 let dir = argv
66 .get(1)
67 .map(std::path::PathBuf::from)
68 .unwrap_or_else(|| "man/man1".into());
69 let n = man::generate(&dir)?;
70 println!("wrote {n} man pages to {}", dir.display());
71 return Ok(0);
72 }
73 _ => {}
74 }
75
76 match alias_command(&program) {
79 Some(_) => cmd::set_prog(
80 &Path::new(&program)
81 .file_name()
82 .unwrap_or_default()
83 .to_string_lossy(),
84 ),
85 None => cmd::set_prog(&format!("xil {first}")),
86 }
87
88 let Some(spec) = commands::find(&first) else {
89 xil_core::log::init("xil");
90 xil_core::log::error(&format!("Unknown command: {first}"));
91 eprint!("{}", commands::help_text());
92 return Ok(2);
93 };
94 let args = &argv[1..];
95
96 if spec.name == "status" && args.iter().any(|a| a == "--toolchain") {
99 let mut out = std::io::stdout().lock();
100 writeln!(out, "rxil {VERSION}")?;
101 write!(out, "{}", workers::describe())?;
102 writeln!(out, "commands: {}", commands::COMMANDS.len())?;
103 return Ok(0);
104 }
105
106 (spec.run)(args)
107}
108
109fn alias_command(program: &OsString) -> Option<String> {
111 let stem = Path::new(program).file_name()?.to_str()?;
112 let stem = stem.strip_suffix(".exe").unwrap_or(stem);
113 let rest = stem.strip_prefix("xil-")?;
114 if rest.is_empty() {
115 return None;
116 }
117 Some(rest.to_string())
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn alias_from_program_name() {
126 assert_eq!(
127 alias_command(&OsString::from("/usr/local/bin/xil-parse")),
128 Some("parse".into())
129 );
130 assert_eq!(
131 alias_command(&OsString::from("xil-sfx-match.exe")),
132 Some("sfx-match".into())
133 );
134 assert_eq!(alias_command(&OsString::from("xil")), None);
135 assert_eq!(alias_command(&OsString::from("xil-")), None);
136 assert_eq!(alias_command(&OsString::from("target/debug/xil")), None);
137 }
138}