A human-friendly DSL for ATProto Lexicons
0
fork

Configure Feed

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

Fixup mlf generation from lexicons

+53 -19
+52 -18
mlf-cli/src/generate/mlf.rs
··· 1 1 use miette::Diagnostic; 2 2 use serde_json::Value; 3 - use std::path::{Path, PathBuf}; 3 + use std::path::PathBuf; 4 4 use thiserror::Error; 5 5 6 6 #[derive(Error, Debug, Diagnostic)] 7 7 pub enum MlfGenerateError { 8 8 #[error("Failed to read file: {path}")] 9 9 #[diagnostic(code(mlf::generate::read_file))] 10 + #[allow(dead_code)] 10 11 ReadFile { 11 12 path: String, 12 13 #[source] ··· 15 16 16 17 #[error("Failed to parse JSON: {path}")] 17 18 #[diagnostic(code(mlf::generate::parse_json))] 19 + #[allow(dead_code)] 18 20 ParseJson { 19 21 path: String, 20 22 #[source] ··· 161 163 pub fn generate_mlf_from_json(json: &Value) -> Result<String, MlfGenerateError> { 162 164 let mut output = String::new(); 163 165 166 + // Extract NSID to get the last segment for "main" definitions 167 + let nsid = json 168 + .get("id") 169 + .and_then(|v| v.as_str()) 170 + .ok_or_else(|| MlfGenerateError::InvalidLexicon { 171 + message: "Missing 'id' field in lexicon".to_string(), 172 + })?; 173 + 174 + let last_segment = nsid.split('.').last().unwrap_or("main"); 175 + 164 176 let defs = json.get("defs").and_then(|v| v.as_object()).ok_or_else(|| { 165 177 MlfGenerateError::InvalidLexicon { 166 178 message: "Missing or invalid 'defs' field".to_string(), ··· 177 189 178 190 match def_type { 179 191 "record" => { 180 - let mlf = generate_record(name, def)?; 192 + let mlf = generate_record(name, def, last_segment)?; 181 193 output.push_str(&mlf); 182 194 output.push('\n'); 183 195 } 184 196 "query" => { 185 - let mlf = generate_query(name, def)?; 197 + let mlf = generate_query(name, def, last_segment)?; 186 198 output.push_str(&mlf); 187 199 output.push('\n'); 188 200 } 189 201 "procedure" => { 190 - let mlf = generate_procedure(name, def)?; 202 + let mlf = generate_procedure(name, def, last_segment)?; 191 203 output.push_str(&mlf); 192 204 output.push('\n'); 193 205 } 194 206 "subscription" => { 195 - let mlf = generate_subscription(name, def)?; 207 + let mlf = generate_subscription(name, def, last_segment)?; 196 208 output.push_str(&mlf); 197 209 output.push('\n'); 198 210 } ··· 215 227 Ok(output) 216 228 } 217 229 218 - fn generate_record(name: &str, def: &Value) -> Result<String, MlfGenerateError> { 230 + /// Reserved words in MLF that need to be escaped 231 + const RESERVED_WORDS: &[&str] = &[ 232 + "main", "record", "query", "procedure", "subscription", "token", "def", "type", "use", 233 + "pub", "alias", "namespace", "constrained", "error", "unit", "null", "boolean", 234 + "integer", "string", "bytes", "blob", "unknown", "array", "object", "union", "ref", 235 + ]; 236 + 237 + /// Escape a name if it's a reserved word 238 + fn escape_name(name: &str) -> String { 239 + if RESERVED_WORDS.contains(&name) { 240 + format!("`{}`", name) 241 + } else { 242 + name.to_string() 243 + } 244 + } 245 + 246 + fn generate_record(name: &str, def: &Value, last_segment: &str) -> Result<String, MlfGenerateError> { 219 247 let mut output = String::new(); 220 248 221 249 // Add doc comment if present ··· 227 255 } 228 256 } 229 257 230 - // Use "main" name if present, otherwise use the definition name 258 + // Use last segment of NSID for "main" definitions 231 259 let record_name = if name == "main" { 232 - // Try to extract the last segment from the namespace ID 233 - // This is a heuristic - we could make it better 234 - "main" 260 + escape_name(last_segment) 235 261 } else { 236 - name 262 + escape_name(name) 237 263 }; 238 264 239 265 output.push_str(&format!("record {} {{\n", record_name)); ··· 286 312 Ok(output) 287 313 } 288 314 289 - fn generate_query(name: &str, def: &Value) -> Result<String, MlfGenerateError> { 315 + fn generate_query(name: &str, def: &Value, last_segment: &str) -> Result<String, MlfGenerateError> { 290 316 let mut output = String::new(); 291 317 292 318 // Add doc comment ··· 298 324 } 299 325 } 300 326 301 - let query_name = if name == "main" { "query" } else { name }; 327 + let query_name = if name == "main" { 328 + escape_name(last_segment) 329 + } else { 330 + escape_name(name) 331 + }; 302 332 output.push_str(&format!("query {}", query_name)); 303 333 304 334 // Parameters ··· 368 398 Ok(output) 369 399 } 370 400 371 - fn generate_procedure(name: &str, def: &Value) -> Result<String, MlfGenerateError> { 401 + fn generate_procedure(name: &str, def: &Value, last_segment: &str) -> Result<String, MlfGenerateError> { 372 402 let mut output = String::new(); 373 403 374 404 // Add doc comment ··· 380 410 } 381 411 } 382 412 383 - let procedure_name = if name == "main" { "procedure" } else { name }; 413 + let procedure_name = if name == "main" { 414 + escape_name(last_segment) 415 + } else { 416 + escape_name(name) 417 + }; 384 418 output.push_str(&format!("procedure {}", procedure_name)); 385 419 386 420 // Input parameters ··· 456 490 Ok(output) 457 491 } 458 492 459 - fn generate_subscription(name: &str, def: &Value) -> Result<String, MlfGenerateError> { 493 + fn generate_subscription(name: &str, def: &Value, last_segment: &str) -> Result<String, MlfGenerateError> { 460 494 let mut output = String::new(); 461 495 462 496 // Add doc comment ··· 469 503 } 470 504 471 505 let subscription_name = if name == "main" { 472 - "subscription" 506 + escape_name(last_segment) 473 507 } else { 474 - name 508 + escape_name(name) 475 509 }; 476 510 output.push_str(&format!("subscription {}", subscription_name)); 477 511
+1 -1
mlf-cli/src/workspace_ext.rs
··· 109 109 } 110 110 111 111 impl WorkspaceExt for Workspace { 112 - fn has_module(&self, namespace: &str) -> bool { 112 + fn has_module(&self, _namespace: &str) -> bool { 113 113 // This requires exposing the modules field or adding a method to mlf-lang 114 114 // For now, we'll just try to add and catch the error 115 115 // TODO: Add a proper has_module method to Workspace