A human-friendly DSL for ATProto Lexicons
0
fork

Configure Feed

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

Format fixes, init command, and updated docs

+333 -25
+18 -4
mlf-cli/src/config.rs
··· 19 19 #[serde(default)] 20 20 pub source: SourceConfig, 21 21 22 - #[serde(default)] 22 + #[serde(default, skip_serializing_if = "Vec::is_empty")] 23 23 pub output: Vec<OutputConfig>, 24 24 25 25 #[serde(default)] 26 - pub dependencies: Vec<String>, 26 + pub dependencies: DependenciesConfig, 27 27 } 28 28 29 29 #[derive(Debug, Serialize, Deserialize)] ··· 50 50 pub directory: String, 51 51 } 52 52 53 + #[derive(Debug, Serialize, Deserialize)] 54 + pub struct DependenciesConfig { 55 + #[serde(default)] 56 + pub dependencies: Vec<String>, 57 + } 58 + 59 + impl Default for DependenciesConfig { 60 + fn default() -> Self { 61 + Self { 62 + dependencies: vec![], 63 + } 64 + } 65 + } 66 + 53 67 impl Default for MlfConfig { 54 68 fn default() -> Self { 55 69 Self { 56 70 source: SourceConfig::default(), 57 71 output: vec![], 58 - dependencies: vec![], 72 + dependencies: DependenciesConfig::default(), 59 73 } 60 74 } 61 75 } ··· 140 154 let config = MlfConfig::default(); 141 155 assert_eq!(config.source.directory, "./lexicons"); 142 156 assert!(config.output.is_empty()); 143 - assert!(config.dependencies.is_empty()); 157 + assert!(config.dependencies.dependencies.is_empty()); 144 158 } 145 159 }
+5 -5
mlf-cli/src/fetch.rs
··· 162 162 let config_path = project_root.join("mlf.toml"); 163 163 let config = MlfConfig::load(&config_path).map_err(FetchError::NoProjectRoot)?; 164 164 165 - if config.dependencies.is_empty() { 165 + if config.dependencies.dependencies.is_empty() { 166 166 println!("No dependencies found in mlf.toml"); 167 167 return Ok(()); 168 168 } 169 169 170 - println!("Fetching {} dependencies...", config.dependencies.len()); 170 + println!("Fetching {} dependencies...", config.dependencies.dependencies.len()); 171 171 172 172 let mut errors = Vec::new(); 173 173 let mut success_count = 0; 174 174 175 - for dep in &config.dependencies { 175 + for dep in &config.dependencies.dependencies { 176 176 println!("\nFetching: {}", dep); 177 177 match fetch_lexicon(dep, project_root) { 178 178 Ok(()) => { ··· 207 207 let config_path = project_root.join("mlf.toml"); 208 208 let mut config = MlfConfig::load(&config_path).map_err(FetchError::NoProjectRoot)?; 209 209 210 - if config.dependencies.contains(&nsid.to_string()) { 210 + if config.dependencies.dependencies.contains(&nsid.to_string()) { 211 211 println!("Dependency '{}' already in mlf.toml", nsid); 212 212 return Ok(()); 213 213 } 214 214 215 - config.dependencies.push(nsid.to_string()); 215 + config.dependencies.dependencies.push(nsid.to_string()); 216 216 config.save(&config_path).map_err(FetchError::NoProjectRoot)?; 217 217 218 218 println!("Added '{}' to dependencies in mlf.toml", nsid);
+58
mlf-cli/src/init.rs
··· 1 + use crate::config::{init_mlf_cache, MlfConfig}; 2 + use std::io::Write; 3 + 4 + pub fn run_init(skip_prompts: bool) -> Result<(), std::io::Error> { 5 + let current_dir = std::env::current_dir()?; 6 + let config_path = current_dir.join("mlf.toml"); 7 + 8 + // Check if mlf.toml already exists 9 + if config_path.exists() { 10 + eprintln!("mlf.toml already exists in current directory"); 11 + eprintln!("Remove it first if you want to reinitialize"); 12 + return Err(std::io::Error::new( 13 + std::io::ErrorKind::AlreadyExists, 14 + "mlf.toml already exists", 15 + )); 16 + } 17 + 18 + if !skip_prompts { 19 + println!("Initialize MLF project in {}?", current_dir.display()); 20 + println!("This will create:"); 21 + println!(" - mlf.toml (project configuration)"); 22 + println!(" - .mlf/ (cache directory for fetched lexicons)"); 23 + println!(); 24 + print!("Continue? (y/n): "); 25 + std::io::stdout().flush()?; 26 + 27 + let mut input = String::new(); 28 + std::io::stdin().read_line(&mut input)?; 29 + 30 + if input.trim().to_lowercase() != "y" { 31 + println!("Cancelled"); 32 + return Ok(()); 33 + } 34 + } 35 + 36 + // Create default mlf.toml 37 + let config = MlfConfig::default(); 38 + config 39 + .save(&config_path) 40 + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; 41 + 42 + println!("✓ Created mlf.toml"); 43 + 44 + // Initialize .mlf directory 45 + init_mlf_cache(&current_dir)?; 46 + println!("✓ Initialized .mlf/ directory"); 47 + 48 + println!(); 49 + println!("Project initialized successfully!"); 50 + println!(); 51 + println!("Next steps:"); 52 + println!(" 1. Create MLF files in ./lexicons/"); 53 + println!(" 2. Fetch dependencies: mlf fetch <namespace> --save"); 54 + println!(" 3. Check your lexicons: mlf check"); 55 + println!(" 4. Generate code: mlf generate"); 56 + 57 + Ok(()) 58 + }
+9
mlf-cli/src/main.rs
··· 7 7 mod config; 8 8 mod fetch; 9 9 mod generate; 10 + mod init; 10 11 mod workspace_ext; 11 12 12 13 // Import optional code generator plugins ··· 30 31 31 32 #[derive(Subcommand)] 32 33 enum Commands { 34 + Init { 35 + #[arg(long, help = "Skip prompts and use defaults")] 36 + yes: bool, 37 + }, 38 + 33 39 Check { 34 40 #[arg(help = "MLF lexicon file(s) to validate (glob patterns supported). If omitted, checks source directory from mlf.toml")] 35 41 input: Vec<String>, ··· 95 101 let cli = Cli::parse(); 96 102 97 103 let result: Result<(), miette::Report> = match cli.command { 104 + Commands::Init { yes } => { 105 + init::run_init(yes).into_diagnostic() 106 + } 98 107 Commands::Check { input } => { 99 108 check::run_check(input).into_diagnostic() 100 109 }
+3 -1
mlf.toml
··· 1 1 output = [] 2 - dependencies = [] 3 2 4 3 [source] 5 4 directory = "./lexicons" 5 + 6 + [dependencies] 7 + dependencies = []
+25 -5
website/content/docs/cli/01-installation.md
··· 67 67 mlf --help 68 68 ``` 69 69 70 + ## Initialize a Project 71 + 72 + After installation, initialize a new MLF project: 73 + 74 + ```bash 75 + mlf init 76 + ``` 77 + 78 + This creates: 79 + - `mlf.toml` - Project configuration file 80 + - `.mlf/` - Cache directory for fetched lexicons (automatically gitignored) 81 + - `./lexicons/` - Default source directory for your MLF files 82 + 83 + Use `--yes` to skip the confirmation prompt: 84 + 85 + ```bash 86 + mlf init --yes 87 + ``` 88 + 70 89 ## Next Steps 71 90 72 - Once installed, you can: 91 + Once initialized, you can: 73 92 74 - 1. [Configure your project](02-configuration.md) with an `mlf.toml` file 75 - 2. [Check MLF files](03-check.md) for syntax and type errors 76 - 3. [Generate code](05-generate.md) in your preferred language 77 - 4. [Fetch remote lexicons](06-fetch.md) from ATProto repositories 93 + 1. Review your [project configuration](02-configuration.md) in `mlf.toml` 94 + 2. [Fetch remote lexicons](06-fetch.md) from ATProto repositories 95 + 3. Create MLF files in `./lexicons/` 96 + 4. [Check MLF files](03-check.md) for syntax and type errors 97 + 5. [Generate code](05-generate.md) in your preferred language
+1 -1
website/content/docs/cli/03-check.md website/content/docs/cli/04-check.md
··· 1 1 +++ 2 2 title = "Check Command" 3 3 description = "Validate MLF lexicon files" 4 - weight = 3 4 + weight = 4 5 5 +++ 6 6 7 7 The `mlf check` command validates MLF lexicon files for syntax and type errors.
+202
website/content/docs/cli/03-init.md
··· 1 + +++ 2 + title = "Init Command" 3 + description = "Initialize a new MLF project" 4 + weight = 3 5 + +++ 6 + 7 + The `mlf init` command creates a new MLF project with default configuration and directory structure. 8 + 9 + ## Usage 10 + 11 + ```bash 12 + # Interactive initialization (prompts for confirmation) 13 + mlf init 14 + 15 + # Skip prompts 16 + mlf init --yes 17 + ``` 18 + 19 + **Options:** 20 + - `--yes` - Skip confirmation prompts and use defaults 21 + 22 + ## What It Does 23 + 24 + Running `mlf init` creates: 25 + 26 + 1. **mlf.toml** - Project configuration file with defaults: 27 + ```toml 28 + [source] 29 + directory = "./lexicons" 30 + 31 + [dependencies] 32 + dependencies = [] 33 + ``` 34 + 35 + 2. **.mlf/** - Cache directory structure: 36 + ``` 37 + .mlf/ 38 + ├── .gitignore # Ignores all files except itself 39 + ├── .lexicon-cache.toml # Metadata about fetched lexicons 40 + └── lexicons/ 41 + ├── json/ # Original JSON lexicons 42 + └── mlf/ # Converted MLF format 43 + ``` 44 + 45 + The `.mlf` directory is automatically added to `.gitignore` so fetched lexicons aren't committed to version control. 46 + 47 + ## Interactive Mode 48 + 49 + Without `--yes`, init prompts for confirmation: 50 + 51 + ```bash 52 + $ mlf init 53 + Initialize MLF project in /path/to/project? 54 + This will create: 55 + - mlf.toml (project configuration) 56 + - .mlf/ (cache directory for fetched lexicons) 57 + 58 + Continue? (y/n): y 59 + ✓ Created mlf.toml 60 + ✓ Initialized .mlf/ directory 61 + 62 + Project initialized successfully! 63 + 64 + Next steps: 65 + 1. Create MLF files in ./lexicons/ 66 + 2. Fetch dependencies: mlf fetch <namespace> --save 67 + 3. Check your lexicons: mlf check 68 + 4. Generate code: mlf generate 69 + ``` 70 + 71 + ## Non-Interactive Mode 72 + 73 + Use `--yes` to skip prompts (useful for scripts and CI/CD): 74 + 75 + ```bash 76 + $ mlf init --yes 77 + ✓ Created mlf.toml 78 + ✓ Initialized .mlf/ directory 79 + 80 + Project initialized successfully! 81 + 82 + Next steps: 83 + 1. Create MLF files in ./lexicons/ 84 + 2. Fetch dependencies: mlf fetch <namespace> --save 85 + 3. Check your lexicons: mlf check 86 + 4. Generate code: mlf generate 87 + ``` 88 + 89 + ## Safety 90 + 91 + Init won't overwrite existing files: 92 + 93 + ```bash 94 + $ mlf init --yes 95 + mlf.toml already exists in current directory 96 + Remove it first if you want to reinitialize 97 + × mlf.toml already exists 98 + ``` 99 + 100 + If you need to reinitialize, remove `mlf.toml` first: 101 + 102 + ```bash 103 + rm mlf.toml 104 + mlf init --yes 105 + ``` 106 + 107 + ## Typical Workflow 108 + 109 + After initialization, a typical workflow looks like: 110 + 111 + ```bash 112 + # 1. Initialize project 113 + mlf init --yes 114 + 115 + # 2. Fetch dependencies 116 + mlf fetch app.bsky --save 117 + mlf fetch com.atproto --save 118 + 119 + # 3. Create your lexicons 120 + mkdir -p lexicons/com/example 121 + cat > lexicons/com/example/post.mlf << 'EOF' 122 + record main { 123 + text!: string constrained { 124 + maxLength: 300, 125 + }, 126 + createdAt!: Datetime, 127 + } 128 + EOF 129 + 130 + # 4. Add output configurations to mlf.toml 131 + cat >> mlf.toml << 'EOF' 132 + 133 + [[output]] 134 + type = "lexicon" 135 + directory = "./dist/lexicons" 136 + 137 + [[output]] 138 + type = "typescript" 139 + directory = "./src/types" 140 + EOF 141 + 142 + # 5. Check for errors 143 + mlf check 144 + 145 + # 6. Generate code 146 + mlf generate 147 + ``` 148 + 149 + ## Manual Setup Alternative 150 + 151 + If you prefer manual setup, you can create these files yourself: 152 + 153 + **mlf.toml:** 154 + ```toml 155 + [source] 156 + directory = "./lexicons" 157 + 158 + [dependencies] 159 + dependencies = [] 160 + 161 + [[output]] 162 + type = "lexicon" 163 + directory = "./dist/lexicons" 164 + ``` 165 + 166 + **Directory structure:** 167 + ```bash 168 + mkdir -p lexicons 169 + mkdir -p .mlf/lexicons/{json,mlf} 170 + echo "*\n!.gitignore\n" > .mlf/.gitignore 171 + ``` 172 + 173 + However, `mlf init` is recommended as it ensures everything is set up correctly. 174 + 175 + ## CI/CD Integration 176 + 177 + Use `mlf init --yes` in CI pipelines: 178 + 179 + ```yaml 180 + # GitHub Actions example 181 + - name: Initialize MLF Project 182 + run: mlf init --yes 183 + 184 + - name: Fetch Dependencies 185 + run: mlf fetch 186 + 187 + - name: Generate Code 188 + run: mlf generate 189 + ``` 190 + 191 + ## Exit Codes 192 + 193 + - `0` - Success 194 + - `1` - Error (e.g., mlf.toml already exists, permission denied) 195 + 196 + ## Tips 197 + 198 + 1. **Always start with init** - It sets up the correct structure 199 + 2. **Use --yes in scripts** - Avoids hanging on prompts 200 + 3. **Commit mlf.toml** - Track your project configuration 201 + 4. **Don't commit .mlf/** - Let each developer fetch dependencies 202 + 5. **Customize after init** - Edit `mlf.toml` to add outputs and dependencies
+1 -1
website/content/docs/cli/04-validate.md website/content/docs/cli/05-validate.md
··· 1 1 +++ 2 2 title = "Validate Command" 3 3 description = "Validate JSON records against lexicons" 4 - weight = 4 4 + weight = 5 5 5 +++ 6 6 7 7 The `mlf validate` command validates JSON record data against an MLF lexicon schema.
+1 -1
website/content/docs/cli/05-generate.md website/content/docs/cli/06-generate.md
··· 1 1 +++ 2 2 title = "Generate Commands" 3 3 description = "Generate code and lexicons from MLF" 4 - weight = 5 4 + weight = 6 5 5 +++ 6 6 7 7 The `mlf generate` command converts MLF files to various output formats including JSON lexicons and code in multiple programming languages.
+1 -1
website/content/docs/cli/06-fetch.md website/content/docs/cli/07-fetch.md
··· 1 1 +++ 2 2 title = "Fetch Command" 3 3 description = "Download lexicons from remote repositories" 4 - weight = 6 4 + weight = 7 5 5 +++ 6 6 7 7 The `mlf fetch` command downloads ATProto lexicons from remote repositories and converts them to MLF format.
+1 -1
website/content/docs/cli/07-errors.md website/content/docs/cli/08-errors.md
··· 1 1 +++ 2 2 title = "Error Messages" 3 3 description = "Understanding MLF error diagnostics" 4 - weight = 7 4 + weight = 8 5 5 +++ 6 6 7 7 MLF provides rich, helpful error messages with source code context and suggestions for fixing issues.
+8 -5
website/content/docs/cli/_index.md
··· 21 21 ## Quick Start 22 22 23 23 ```bash 24 - # Check MLF files 25 - mlf check 26 - 27 - # Generate TypeScript types 28 - mlf generate code -g typescript -i "lexicons/**/*.mlf" -o src/types/ 24 + # Initialize a new project 25 + mlf init 29 26 30 27 # Fetch remote lexicons 31 28 mlf fetch stream.place --save 29 + 30 + # Check MLF files 31 + mlf check 32 32 33 33 # Generate all configured outputs 34 34 mlf generate ··· 41 41 See the [Configuration](02-configuration.md) section for details. 42 42 43 43 ## Command Categories 44 + 45 + ### Project Setup 46 + - `mlf init` - Initialize a new MLF project 44 47 45 48 ### Validation Commands 46 49 - `mlf check` - Validate MLF syntax and types