A human-friendly DSL for ATProto Lexicons
0
fork

Configure Feed

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

Fix open unions

+28 -12
+10 -5
mlf-codegen/src/lib.rs
··· 423 423 "items": generate_type_json(inner, usage_counts, workspace, current_namespace) 424 424 }) 425 425 } 426 - Type::Union { types, .. } => { 426 + Type::Union { types, closed, .. } => { 427 427 let refs: Vec<Value> = types 428 428 .iter() 429 429 .map(|t| generate_type_json(t, usage_counts, workspace, current_namespace)) 430 430 .collect(); 431 - json!({ 432 - "type": "union", 433 - "refs": refs 434 - }) 431 + let mut union_obj = Map::new(); 432 + union_obj.insert("type".to_string(), json!("union")); 433 + union_obj.insert("refs".to_string(), json!(refs)); 434 + // Only emit "closed" field if true (closed unions) 435 + // Open unions omit the field (defaults to false per ATProto spec) 436 + if *closed { 437 + union_obj.insert("closed".to_string(), json!(true)); 438 + } 439 + Value::Object(union_obj) 435 440 } 436 441 Type::Object { fields, .. } => { 437 442 let mut required = Vec::new();
+1 -1
mlf-lang/src/ast.rs
··· 249 249 /// Array type 250 250 Array { inner: Box<Type>, span: Span }, 251 251 /// Union type 252 - Union { types: Vec<Type>, span: Span }, 252 + Union { types: Vec<Type>, closed: bool, span: Span }, 253 253 /// Object type (inline) 254 254 Object { fields: Vec<Field>, span: Span }, 255 255 /// Parenthesized type (for grouping, e.g., (A | B)[])
+1 -1
mlf-lang/src/lexer.rs
··· 283 283 map(string_literal, Some), 284 284 map(raw_identifier, Some), 285 285 map(type_ident, Some), 286 + map(symbol, Some), // Parse symbols before identifiers so _ is caught 286 287 map(identifier, Some), 287 - map(symbol, Some), 288 288 )).parse(input) 289 289 } 290 290
+15 -4
mlf-lang/src/parser.rs
··· 411 411 } 412 412 413 413 let span = Span::new(types[0].span().start, types.last().unwrap().span().end); 414 - ReturnType::Type(Type::Union { types, span }) 414 + // Return type unions are closed by default (no _ support in return types yet) 415 + ReturnType::Type(Type::Union { types, closed: true, span }) 415 416 } 416 417 } else { 417 418 ReturnType::Type(output) ··· 462 463 } 463 464 464 465 let span = Span::new(types[0].span().start, types.last().unwrap().span().end); 465 - ReturnType::Type(Type::Union { types, span }) 466 + // Return type unions are closed by default (no _ support in return types yet) 467 + ReturnType::Type(Type::Union { types, closed: true, span }) 466 468 } 467 469 } else { 468 470 ReturnType::Type(output) ··· 623 625 if matches!(self.current().token, LexToken::Pipe) { 624 626 let mut types = alloc::vec![base]; 625 627 628 + let mut has_underscore = false; 626 629 while matches!(self.current().token, LexToken::Pipe) { 627 630 self.advance(); 628 631 if matches!(self.current().token, LexToken::Error) { 629 632 break; 630 633 } 631 - types.push(self.parse_base_type()?); 634 + // Check if this is an underscore (open union marker) 635 + if matches!(self.current().token, LexToken::Underscore) { 636 + has_underscore = true; 637 + self.advance(); 638 + } else { 639 + types.push(self.parse_base_type()?); 640 + } 632 641 } 633 642 634 643 let span = Span::new(types[0].span().start, types.last().unwrap().span().end); 635 - return Ok(Type::Union { types, span }); 644 + // Unions are closed by default, open if _ is present 645 + let closed = !has_underscore; 646 + return Ok(Type::Union { types, closed, span }); 636 647 } 637 648 638 649 Ok(base)
+1 -1
mlf-lang/src/workspace.rs
··· 251 251 Type::Primitive { .. } | Type::Unknown { .. } => Ok(()), 252 252 Type::Reference { .. } => Ok(()), 253 253 Type::Array { inner, .. } => self.typecheck_type(namespace, inner), 254 - Type::Union { types, span } => { 254 + Type::Union { types, span, .. } => { 255 255 let mut errors = ValidationErrors::new(); 256 256 257 257 for ty in types {