fork
Configure Feed
Select the types of activity you want to include in your feed.
A better Rust ATProto crate
fork
Configure Feed
Select the types of activity you want to include in your feed.
1use clap::Parser;
2use jacquard_lexgen::cli::LexFetchArgs;
3use jacquard_lexgen::fetch::{Config, Fetcher};
4use jacquard_lexicon::codegen::{CodeGenerator, CodegenMode};
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 mode = if args.macro_mode {
57 CodegenMode::Macro
58 } else {
59 CodegenMode::Pretty
60 };
61 let corpus = LexiconCorpus::load_from_dir(&config.output.lexicons_dir)?;
62 let codegen = CodeGenerator::with_mode(&corpus, "crate".to_string(), mode);
63 std::fs::create_dir_all(&config.output.codegen_dir).into_diagnostic()?;
64 codegen.write_to_disk(&config.output.codegen_dir)?;
65
66 println!("Generated code to {:?}", config.output.codegen_dir);
67
68 // Update Cargo.toml features if cargo_toml_path is specified
69 if let Some(cargo_toml_path) = &config.output.cargo_toml_path {
70 if args.verbose {
71 println!("Updating Cargo.toml features...");
72 }
73
74 update_cargo_features(&codegen, cargo_toml_path, &config.output.codegen_dir)?;
75 println!("Updated features in {:?}", cargo_toml_path);
76 }
77 } else {
78 println!("Lexicons written to {:?}", config.output.lexicons_dir);
79 }
80
81 Ok(())
82}
83
84fn update_cargo_features(
85 codegen: &CodeGenerator,
86 cargo_toml_path: &PathBuf,
87 codegen_dir: &PathBuf,
88) -> Result<()> {
89 // Read existing Cargo.toml
90 let content = std::fs::read_to_string(cargo_toml_path).into_diagnostic()?;
91
92 // Find the "# --- generated ---" marker
93 const MARKER: &str = "# --- generated ---";
94
95 let (before, _after) = content
96 .split_once(MARKER)
97 .ok_or_else(|| miette::miette!("Cargo.toml missing '{}' marker", MARKER))?;
98
99 // Generate new features, passing lib.rs path to detect existing modules
100 let lib_rs_path = codegen_dir.join("lib.rs");
101 let features = codegen.generate_cargo_features(Some(&lib_rs_path));
102
103 // Reconstruct file
104 let new_content = format!("{}{}\n{}", before, MARKER, features);
105
106 // Write back
107 std::fs::write(cargo_toml_path, new_content).into_diagnostic()?;
108
109 Ok(())
110}