es_fluent_cli_helpers/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod cli;
4mod generate;
5
6use std::path::Path;
7
8pub use cli::{ExpectedKey, InventoryData, write_inventory_for_crate};
9pub use generate::{EsFluentGenerator, FluentParseMode, GeneratorArgs, GeneratorError};
10
11/// Run the FTL generation process for a crate.
12///
13/// This function:
14/// - Reads the i18n.toml configuration
15/// - Resolves output and assets paths
16/// - Runs the es-fluent generator
17/// - Writes the result status to result.json
18///
19/// Returns `true` if any FTL files were modified, `false` otherwise.
20pub fn run_generate(i18n_toml_path: &str, crate_name: &str) -> bool {
21    // Read config from parent crate's i18n.toml
22    let i18n_toml_path = Path::new(i18n_toml_path);
23    let config = es_fluent_toml::I18nConfig::read_from_path(i18n_toml_path)
24        .expect("Failed to read i18n.toml");
25
26    let i18n_dir = i18n_toml_path
27        .parent()
28        .expect("Failed to get i18n directory");
29    let assets_dir = i18n_dir.join(&config.assets_dir);
30    let output_path = assets_dir.join(&config.fallback_language);
31
32    let changed = EsFluentGenerator::builder()
33        .output_path(output_path)
34        .assets_dir(assets_dir)
35        .crate_name(crate_name)
36        .build()
37        .run_cli()
38        .expect("Failed to run generator");
39
40    // Write result to JSON file for CLI to read
41    let result = serde_json::json!({ "changed": changed });
42
43    let metadata_dir = std::path::Path::new("metadata").join(crate_name);
44    std::fs::create_dir_all(&metadata_dir).expect("Failed to create metadata directory");
45
46    std::fs::write(
47        metadata_dir.join("result.json"),
48        serde_json::to_string(&result).unwrap(),
49    )
50    .expect("Failed to write result file");
51
52    changed
53}
54
55/// Run the FTL generation process with explicit options (no CLI parsing).
56///
57/// This is used by the monolithic binary to avoid conflicting clap argument parsing.
58pub fn run_generate_with_options(
59    i18n_toml_path: &str,
60    crate_name: &str,
61    mode: FluentParseMode,
62    dry_run: bool,
63) -> bool {
64    let i18n_toml_path = Path::new(i18n_toml_path);
65    let config = es_fluent_toml::I18nConfig::read_from_path(i18n_toml_path)
66        .expect("Failed to read i18n.toml");
67
68    let i18n_dir = i18n_toml_path
69        .parent()
70        .expect("Failed to get i18n directory");
71    let assets_dir = i18n_dir.join(&config.assets_dir);
72    let output_path = assets_dir.join(&config.fallback_language);
73
74    let changed = EsFluentGenerator::builder()
75        .output_path(output_path)
76        .assets_dir(assets_dir)
77        .crate_name(crate_name)
78        .mode(mode)
79        .dry_run(dry_run)
80        .build()
81        .generate()
82        .expect("Failed to run generator");
83
84    let result = serde_json::json!({ "changed": changed });
85
86    let metadata_dir = std::path::Path::new("metadata").join(crate_name);
87    std::fs::create_dir_all(&metadata_dir).expect("Failed to create metadata directory");
88
89    std::fs::write(
90        metadata_dir.join("result.json"),
91        serde_json::to_string(&result).unwrap(),
92    )
93    .expect("Failed to write result file");
94
95    changed
96}
97
98/// Run the inventory check process for a crate.
99///
100/// This writes the collected inventory data for the specified crate.
101pub fn run_check(crate_name: &str) {
102    write_inventory_for_crate(crate_name);
103}
104
105/// Run the FTL clean process with explicit options (no CLI parsing).
106///
107/// This is used by the monolithic binary to avoid conflicting clap argument parsing.
108pub fn run_clean_with_options(
109    i18n_toml_path: &str,
110    crate_name: &str,
111    all_locales: bool,
112    dry_run: bool,
113) -> bool {
114    let i18n_toml_path = Path::new(i18n_toml_path);
115    let config = es_fluent_toml::I18nConfig::read_from_path(i18n_toml_path)
116        .expect("Failed to read i18n.toml");
117
118    let i18n_dir = i18n_toml_path
119        .parent()
120        .expect("Failed to get i18n directory");
121    let assets_dir = i18n_dir.join(&config.assets_dir);
122    let output_path = assets_dir.join(&config.fallback_language);
123
124    let changed = EsFluentGenerator::builder()
125        .output_path(output_path)
126        .assets_dir(assets_dir)
127        .crate_name(crate_name)
128        .dry_run(dry_run)
129        .build()
130        .clean(all_locales, dry_run)
131        .expect("Failed to run clean");
132
133    let result = serde_json::json!({ "changed": changed });
134
135    let metadata_dir = std::path::Path::new("metadata").join(crate_name);
136    std::fs::create_dir_all(&metadata_dir).expect("Failed to create metadata directory");
137
138    std::fs::write(
139        metadata_dir.join("result.json"),
140        serde_json::to_string(&result).unwrap(),
141    )
142    .expect("Failed to write result file");
143
144    changed
145}
146
147/// Main entry point for the monolithic binary.
148///
149/// Parses command-line arguments and dispatches to the appropriate handler.
150/// This minimizes the code needed in the generated binary template.
151pub fn run() {
152    let args: Vec<String> = std::env::args().collect();
153
154    let command = args.get(1).map(|s| s.as_str()).unwrap_or("check");
155    let i18n_path = args.get(2).map(|s| s.as_str());
156
157    let target_crate = args
158        .iter()
159        .position(|s| s == "--crate")
160        .and_then(|i| args.get(i + 1))
161        .map(|s| s.as_str());
162
163    let mode_str = args
164        .iter()
165        .position(|s| s == "--mode")
166        .and_then(|i| args.get(i + 1))
167        .map(|s| s.as_str())
168        .unwrap_or("conservative");
169
170    let dry_run = args.iter().any(|s| s == "--dry-run");
171    let all_locales = args.iter().any(|s| s == "--all");
172
173    match command {
174        "generate" => {
175            let path = i18n_path.expect("Missing i18n.toml path");
176            let name = target_crate.expect("Missing --crate argument");
177            let mode = match mode_str {
178                "aggressive" => FluentParseMode::Aggressive,
179                _ => FluentParseMode::Conservative,
180            };
181            run_generate_with_options(path, name, mode, dry_run);
182        },
183        "clean" => {
184            let path = i18n_path.expect("Missing i18n.toml path");
185            let name = target_crate.expect("Missing --crate argument");
186            run_clean_with_options(path, name, all_locales, dry_run);
187        },
188        "check" => {
189            let name = target_crate.expect("Missing --crate argument");
190            run_check(name);
191        },
192        _ => {
193            eprintln!("Unknown command: {}", command);
194            std::process::exit(1);
195        },
196    }
197}