A better Rust ATProto crate
at main 100 lines 3.4 kB view raw
1use clap::Parser; 2use jacquard_lexgen::cli::LexFetchArgs; 3use jacquard_lexgen::fetch::{Config, Fetcher}; 4use jacquard_lexicon::codegen::CodeGenerator; 5use jacquard_lexicon::corpus::LexiconCorpus; 6use miette::{IntoDiagnostic, Result}; 7use std::path::PathBuf; 8 9#[tokio::main] 10async fn main() -> Result<()> { 11 let args = LexFetchArgs::parse(); 12 13 if args.verbose { 14 println!("Reading config from {:?}...", args.config); 15 } 16 17 let config_text = std::fs::read_to_string(&args.config).into_diagnostic()?; 18 19 // Parse KDL config 20 let config = Config::from_kdl(&config_text)?; 21 22 // Fetch from all sources 23 if args.verbose { 24 println!("Fetching lexicons from {} sources...", config.sources.len()); 25 } 26 27 let fetcher = Fetcher::new(config.clone()); 28 let lexicons = fetcher.fetch_all(args.verbose).await?; 29 30 if args.verbose || !args.no_codegen { 31 println!("Fetched {} unique lexicons", lexicons.len()); 32 } 33 34 // Ensure output directory exists 35 std::fs::create_dir_all(&config.output.lexicons_dir).into_diagnostic()?; 36 37 // Write each lexicon to a file 38 for (nsid, doc) in &lexicons { 39 let filename = format!("{}.json", nsid.replace('.', "_")); 40 let path = config.output.lexicons_dir.join(&filename); 41 42 let json = serde_json::to_string_pretty(doc).into_diagnostic()?; 43 std::fs::write(&path, json).into_diagnostic()?; 44 45 if args.verbose { 46 println!("Wrote {}", filename); 47 } 48 } 49 50 // Run codegen if requested 51 if !args.no_codegen { 52 if args.verbose { 53 println!("Generating code..."); 54 } 55 56 let corpus = LexiconCorpus::load_from_dir(&config.output.lexicons_dir)?; 57 let codegen = CodeGenerator::new(&corpus, "crate".to_string()); 58 std::fs::create_dir_all(&config.output.codegen_dir).into_diagnostic()?; 59 codegen.write_to_disk(&config.output.codegen_dir)?; 60 61 println!("Generated code to {:?}", config.output.codegen_dir); 62 63 // Update Cargo.toml features if cargo_toml_path is specified 64 if let Some(cargo_toml_path) = &config.output.cargo_toml_path { 65 if args.verbose { 66 println!("Updating Cargo.toml features..."); 67 } 68 69 update_cargo_features(&codegen, cargo_toml_path, &config.output.codegen_dir)?; 70 println!("Updated features in {:?}", cargo_toml_path); 71 } 72 } else { 73 println!("Lexicons written to {:?}", config.output.lexicons_dir); 74 } 75 76 Ok(()) 77} 78 79fn update_cargo_features(codegen: &CodeGenerator, cargo_toml_path: &PathBuf, codegen_dir: &PathBuf) -> Result<()> { 80 // Read existing Cargo.toml 81 let content = std::fs::read_to_string(cargo_toml_path).into_diagnostic()?; 82 83 // Find the "# --- generated ---" marker 84 const MARKER: &str = "# --- generated ---"; 85 86 let (before, _after) = content.split_once(MARKER) 87 .ok_or_else(|| miette::miette!("Cargo.toml missing '{}' marker", MARKER))?; 88 89 // Generate new features, passing lib.rs path to detect existing modules 90 let lib_rs_path = codegen_dir.join("lib.rs"); 91 let features = codegen.generate_cargo_features(Some(&lib_rs_path)); 92 93 // Reconstruct file 94 let new_content = format!("{}{}\n{}", before, MARKER, features); 95 96 // Write back 97 std::fs::write(cargo_toml_path, new_content).into_diagnostic()?; 98 99 Ok(()) 100}