1mod cache;
4mod generation;
5mod identifiers;
6mod output_tree;
7mod ownership;
8mod spec_file;
9
10use std::process::ExitCode;
11
12use cache::{CodegenCache, ManifestInputs};
13use clap::Parser;
14use generation::generate_all_incremental;
15use output_tree::{check_freshness, write_generated_files};
16use wowlab_common::{cli, output};
17use wowlab_fs::path::PathBuf;
18use wowlab_manifest_schema::ManifestRepository;
19
20#[derive(Parser)]
21#[command(name = "codegen", about = "Wowlab spec codegen tool")]
22struct Args {
23 #[arg(long)]
25 manifests: PathBuf,
26
27 #[arg(long)]
29 output: PathBuf,
30
31 #[arg(long, short)]
33 quiet: bool,
34
35 #[arg(long)]
37 check: bool,
38}
39
40fn run(args: &Args) -> anyhow::Result<ExitCode> {
41 let app = cli::boot(
42 "codegen",
43 env!("CARGO_PKG_VERSION"),
44 args.quiet || args.check,
45 "CODEGEN_ROOT",
46 );
47 let repository = ManifestRepository::new(args.manifests.clone());
48 let manifest_paths = repository.toml_paths()?;
49 let inputs = ManifestInputs::capture(&args.manifests, &manifest_paths)?;
50 let cache = CodegenCache::open(&app.root, &args.manifests, &args.output, &inputs);
51
52 if cache.is_complete_hit()? {
53 if !args.quiet || args.check {
54 output::success("generated code is up-to-date");
55 }
56
57 return Ok(ExitCode::SUCCESS);
58 }
59
60 let generation = generate_all_incremental(&args.manifests, &inputs, Some(&cache))?;
61
62 if args.check {
63 let exit_code = check_freshness(&generation.files, &args.output)?;
64
65 if exit_code == ExitCode::SUCCESS {
66 persist_cache(&cache, &generation, args.quiet)?;
67 }
68
69 return Ok(exit_code);
70 }
71
72 write_generated_files(&generation.files, &args.output, args.quiet)?;
73 persist_cache(&cache, &generation, args.quiet)?;
74
75 if !args.quiet {
76 let spec_count = generation
77 .files
78 .iter()
79 .filter(|(file, _)| file.starts_with("specs/") && file != "specs/mod.rs")
80 .count();
81
82 output::success(&format!(
83 "wrote {} spec files + specs/mod.rs + items/mod.rs + mod.rs to {}",
84 spec_count,
85 args.output.display()
86 ));
87 }
88
89 Ok(ExitCode::SUCCESS)
90}
91
92fn persist_cache(
93 cache: &CodegenCache,
94 generation: &generation::GenerationResult,
95 quiet: bool,
96) -> anyhow::Result<()> {
97 let Some(update) = cache.prepare_update(generation)? else {
98 return Ok(());
99 };
100
101 if let Err(error) = update.persist()
102 && !quiet
103 {
104 output::warning(&format!("codegen cache was not persisted: {error:#}"));
105 }
106
107 Ok(())
108}
109
110pub(crate) fn main() -> ExitCode {
111 let args = Args::parse();
112
113 match run(&args) {
114 Ok(code) => code,
115 Err(error) => {
116 output::error(&format!("{error:#}"));
117
118 ExitCode::FAILURE
119 }
120 }
121}
122
123#[cfg(test)]
124#[path = "app/tests.rs"]
125mod tests;