A human-friendly DSL for ATProto Lexicons
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 153 lines 5.0 kB view raw
1use clap::{Parser, Subcommand}; 2use miette::IntoDiagnostic; 3use std::path::PathBuf; 4use std::process; 5 6mod check; 7mod config; 8mod fetch; 9mod generate; 10mod init; 11mod workspace_ext; 12 13// Import optional code generator plugins 14// These are automatically registered via inventory when the feature is enabled 15#[cfg(feature = "typescript")] 16use mlf_codegen_typescript as _; 17 18#[cfg(feature = "go")] 19use mlf_codegen_go as _; 20 21#[cfg(feature = "rust")] 22use mlf_codegen_rust as _; 23 24#[derive(Parser)] 25#[command(name = "mlf")] 26#[command(about = "MLF (Matt's Lexicon Format) CLI tool", long_about = None)] 27struct Cli { 28 #[command(subcommand)] 29 command: Commands, 30} 31 32#[derive(Subcommand)] 33enum Commands { 34 Init { 35 #[arg(long, help = "Skip prompts and use defaults")] 36 yes: bool, 37 }, 38 39 Check { 40 #[arg(help = "MLF lexicon file(s) or directory to validate. If omitted, checks source directory from mlf.toml")] 41 input: Vec<PathBuf>, 42 43 #[arg(long, help = "Root directory for namespace calculation (defaults to mlf.toml source directory or current directory)")] 44 root: Option<PathBuf>, 45 }, 46 47 Validate { 48 #[arg(help = "MLF lexicon file")] 49 lexicon: PathBuf, 50 51 #[arg(help = "JSON record file to validate against the lexicon")] 52 record: PathBuf, 53 }, 54 55 Generate { 56 #[command(subcommand)] 57 command: Option<GenerateCommands>, 58 }, 59 60 Fetch { 61 #[arg(help = "Namespace to fetch (e.g., stream.place). If omitted, fetches all dependencies from mlf.toml")] 62 nsid: Option<String>, 63 64 #[arg(long, help = "Add namespace to dependencies in mlf.toml")] 65 save: bool, 66 67 #[arg(long, help = "Update dependencies to latest versions (ignores lockfile)")] 68 update: bool, 69 70 #[arg(long, help = "Require lockfile and fail if dependencies need updating")] 71 locked: bool, 72 }, 73} 74 75#[derive(Subcommand)] 76enum GenerateCommands { 77 Lexicon { 78 #[arg(short, long, help = "Input MLF file(s) or directory. If omitted, uses source directory from mlf.toml")] 79 input: Vec<PathBuf>, 80 81 #[arg(short, long, help = "Output directory. If omitted, uses first lexicon output from mlf.toml")] 82 output: Option<PathBuf>, 83 84 #[arg(long, help = "Root directory for namespace calculation (defaults to mlf.toml source directory or current directory)")] 85 root: Option<PathBuf>, 86 87 #[arg(long, help = "Use flat file structure (e.g., app.bsky.post.json)")] 88 flat: bool, 89 }, 90 Code { 91 #[arg(short, long, help = "Generator to use (typescript, go, rust, etc.). If omitted, uses first code output from mlf.toml")] 92 generator: Option<String>, 93 94 #[arg(short, long, help = "Input MLF file(s) or directory. If omitted, uses source directory from mlf.toml")] 95 input: Vec<PathBuf>, 96 97 #[arg(short, long, help = "Output directory. If omitted, uses matching output from mlf.toml")] 98 output: Option<PathBuf>, 99 100 #[arg(long, help = "Root directory for namespace calculation (defaults to mlf.toml source directory or current directory)")] 101 root: Option<PathBuf>, 102 103 #[arg(long, help = "Use flat file structure (e.g., app.bsky.post.ts)")] 104 flat: bool, 105 }, 106 Mlf { 107 #[arg(short, long, help = "Input JSON lexicon files (glob patterns supported)")] 108 input: Vec<String>, 109 110 #[arg(short, long, help = "Output directory. If omitted, uses first mlf output from mlf.toml")] 111 output: Option<PathBuf>, 112 }, 113} 114 115#[tokio::main] 116async fn main() { 117 let cli = Cli::parse(); 118 119 let result: Result<(), miette::Report> = match cli.command { 120 Commands::Init { yes } => { 121 init::run_init(yes).into_diagnostic() 122 } 123 Commands::Check { input, root } => { 124 check::run_check(input, root).into_diagnostic() 125 } 126 Commands::Validate { lexicon, record } => { 127 check::validate(lexicon, record).into_diagnostic() 128 } 129 Commands::Generate { command } => match command { 130 Some(GenerateCommands::Lexicon { input, output, root, flat }) => { 131 generate::lexicon::run(input, output, root, flat).into_diagnostic() 132 } 133 Some(GenerateCommands::Code { generator, input, output, root, flat }) => { 134 generate::code::run(generator, input, output, root, flat).into_diagnostic() 135 } 136 Some(GenerateCommands::Mlf { input, output }) => { 137 generate::mlf::run(input, output).into_diagnostic() 138 } 139 None => { 140 // Run all outputs from mlf.toml 141 generate::run_all().into_diagnostic() 142 } 143 }, 144 Commands::Fetch { nsid, save, update, locked } => { 145 fetch::run_fetch(nsid, save, update, locked).await.into_diagnostic() 146 } 147 }; 148 149 if let Err(e) = result { 150 eprintln!("{:?}", e); 151 process::exit(1); 152 } 153}