Skip to main content

tablegen/
main.rs

1//! Generates the studio bulk-table list and resolver interface from Rust sources.
2
3#[global_allocator]
4static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
5
6mod resolver;
7
8use std::process::ExitCode;
9
10use anyhow::ensure;
11use clap::Parser;
12use wowlab_common::{cli, output};
13use wowlab_fs::{
14    artifact::{GeneratedTextFile, Status},
15    path::{Path, PathBuf},
16};
17use wowlab_types::table_registry::PUBLISHED_SNAPSHOT_TABLES;
18
19const RESOLVER_OUTPUT: &str = "game-data-resolver.generated.ts";
20
21#[derive(Parser)]
22#[command(
23    name = "tablegen",
24    about = "Generate the studio bulk-table list from the snapshot registry"
25)]
26struct Args {
27    /// Path to the generated TypeScript file.
28    #[arg(long)]
29    output: PathBuf,
30
31    /// Suppress output except for errors.
32    #[arg(long, short)]
33    quiet: bool,
34
35    /// Check that the generated file is up-to-date without writing. Exits non-zero if stale.
36    #[arg(long)]
37    check: bool,
38}
39
40// The historical source path is part of the checked-in generated-byte contract.
41const HEADER: &str = "// @generated by tablegen from the Rust snapshot registry (crates/cli) -- do not edit by hand.";
42
43fn generate() -> String {
44    let mut out = String::with_capacity(2048);
45
46    out.push_str(HEADER);
47    out.push_str(
48        "\n\nexport type BulkTable = {\n  \
49         readonly key: readonly [string, ...string[]];\n  \
50         readonly name: string;\n};\n\nexport const BULK_TABLES = [\n",
51    );
52
53    for &table in PUBLISHED_SNAPSHOT_TABLES {
54        let name = table.database_name().trim_start_matches("game.");
55        let key = table
56            .snapshot_key()
57            .iter()
58            .map(|col| format!("\"{col}\""))
59            .collect::<Vec<_>>()
60            .join(", ");
61
62        out.push_str("  {\n    key: [");
63        out.push_str(&key);
64        out.push_str("],\n    name: \"");
65        out.push_str(name);
66        out.push_str("\",\n  },\n");
67    }
68
69    out.push_str(
70        "] as const satisfies readonly BulkTable[];\n\n\
71         export type BulkTableName = (typeof BULK_TABLES)[number][\"name\"];\n",
72    );
73
74    out
75}
76
77fn run(args: &Args) -> anyhow::Result<ExitCode> {
78    let _app = cli::boot(
79        "tablegen",
80        env!("CARGO_PKG_VERSION"),
81        args.quiet || args.check,
82        "TABLEGEN_ROOT",
83    );
84
85    let resolver_output = args
86        .output
87        .parent()
88        .unwrap_or_else(|| Path::new("."))
89        .join(RESOLVER_OUTPUT);
90
91    ensure!(
92        args.output != resolver_output,
93        "--output must not use the reserved resolver filename {RESOLVER_OUTPUT}"
94    );
95
96    let outputs = [
97        (args.output.clone(), generate()),
98        (resolver_output, resolver::generate()?),
99    ];
100
101    if args.check {
102        let mut stale = false;
103
104        for (path, generated) in &outputs {
105            if GeneratedTextFile::new(path, generated).status()? != Status::Current {
106                stale = true;
107                output::error(&format!(
108                    "{} is stale, run `cargo tablegen` to regenerate",
109                    path.display()
110                ));
111            }
112        }
113
114        if stale {
115            return Ok(ExitCode::FAILURE);
116        }
117
118        output::success("generated studio files are up-to-date");
119
120        return Ok(ExitCode::SUCCESS);
121    }
122
123    for (path, generated) in &outputs {
124        GeneratedTextFile::new(path, generated).persist()?;
125
126        if !args.quiet {
127            output::success(&format!("wrote {}", path.display()));
128        }
129    }
130
131    Ok(ExitCode::SUCCESS)
132}
133
134fn main() -> ExitCode {
135    let args = Args::parse();
136
137    match run(&args) {
138        Ok(code) => code,
139        Err(e) => {
140            output::error(&format!("{e:#}"));
141
142            ExitCode::FAILURE
143        }
144    }
145}