A human-friendly DSL for ATProto Lexicons

Update lexicon generation examples and fix xrpc errors

+60 -29
+34 -22
mlf-codegen/src/lib.rs
··· 405 405 let output_encoding = get_encoding_annotation(&query.annotations, "output") 406 406 .unwrap_or_else(|| "application/json".to_string()); 407 407 408 - let output = match &query.returns { 409 - ReturnType::None { .. } => None, 408 + let (output, errors) = match &query.returns { 409 + ReturnType::None { .. } => (None, None), 410 410 ReturnType::Type(ty) => { 411 411 let mut output_obj = Map::new(); 412 412 output_obj.insert("encoding".to_string(), json!(output_encoding)); 413 413 output_obj.insert("schema".to_string(), generate_type_json(ty, usage_counts, workspace, current_namespace)); 414 - Some(Value::Object(output_obj)) 414 + (Some(Value::Object(output_obj)), None) 415 415 } 416 416 ReturnType::TypeWithErrors { success, errors, .. } => { 417 - let mut error_defs = Map::new(); 417 + let mut error_array = Vec::new(); 418 418 for error in errors { 419 - error_defs.insert( 420 - error.name.name.clone(), 419 + let error_docs = extract_docs(&error.docs); 420 + let error_obj = if error_docs.is_empty() { 421 + json!({ "name": error.name.name.clone() }) 422 + } else { 421 423 json!({ 422 - "description": extract_docs(&error.docs) 423 - }), 424 - ); 424 + "name": error.name.name.clone(), 425 + "description": error_docs 426 + }) 427 + }; 428 + error_array.push(error_obj); 425 429 } 426 430 427 431 let mut output_obj = Map::new(); 428 432 output_obj.insert("encoding".to_string(), json!(output_encoding)); 429 433 output_obj.insert("schema".to_string(), generate_type_json(success, usage_counts, workspace, current_namespace)); 430 - output_obj.insert("errors".to_string(), json!(error_defs)); 431 - Some(Value::Object(output_obj)) 434 + (Some(Value::Object(output_obj)), Some(Value::Array(error_array))) 432 435 } 433 436 }; 434 437 ··· 438 441 query_obj.insert("parameters".to_string(), params); 439 442 if let Some(output_val) = output { 440 443 query_obj.insert("output".to_string(), output_val); 444 + } 445 + if let Some(errors_val) = errors { 446 + query_obj.insert("errors".to_string(), errors_val); 441 447 } 442 448 Value::Object(query_obj) 443 449 } ··· 482 488 let output_encoding = get_encoding_annotation(&procedure.annotations, "output") 483 489 .unwrap_or_else(|| "application/json".to_string()); 484 490 485 - let output = match &procedure.returns { 486 - ReturnType::None { .. } => None, 491 + let (output, errors) = match &procedure.returns { 492 + ReturnType::None { .. } => (None, None), 487 493 ReturnType::Type(ty) => { 488 494 let mut output_obj = Map::new(); 489 495 output_obj.insert("encoding".to_string(), json!(output_encoding)); 490 496 output_obj.insert("schema".to_string(), generate_type_json(ty, usage_counts, workspace, current_namespace)); 491 - Some(Value::Object(output_obj)) 497 + (Some(Value::Object(output_obj)), None) 492 498 } 493 499 ReturnType::TypeWithErrors { success, errors, .. } => { 494 - let mut error_defs = Map::new(); 500 + let mut error_array = Vec::new(); 495 501 for error in errors { 496 - error_defs.insert( 497 - error.name.name.clone(), 502 + let error_docs = extract_docs(&error.docs); 503 + let error_obj = if error_docs.is_empty() { 504 + json!({ "name": error.name.name.clone() }) 505 + } else { 498 506 json!({ 499 - "description": extract_docs(&error.docs) 500 - }), 501 - ); 507 + "name": error.name.name.clone(), 508 + "description": error_docs 509 + }) 510 + }; 511 + error_array.push(error_obj); 502 512 } 503 513 504 514 let mut output_obj = Map::new(); 505 515 output_obj.insert("encoding".to_string(), json!(output_encoding)); 506 516 output_obj.insert("schema".to_string(), generate_type_json(success, usage_counts, workspace, current_namespace)); 507 - output_obj.insert("errors".to_string(), json!(error_defs)); 508 - Some(Value::Object(output_obj)) 517 + (Some(Value::Object(output_obj)), Some(Value::Array(error_array))) 509 518 } 510 519 }; 511 520 ··· 517 526 } 518 527 if let Some(output_val) = output { 519 528 result.insert("output".to_string(), output_val); 529 + } 530 + if let Some(errors_val) = errors { 531 + result.insert("errors".to_string(), errors_val); 520 532 } 521 533 Value::Object(result) 522 534 }
+5 -4
website/content/_index.md
··· 23 23 24 24 json_example = '''```json 25 25 { 26 + "$type": "com.atproto.lexicon.schema", 26 27 "lexicon": 1, 27 28 "id": "com.example.thread", 28 29 "defs": { ··· 36 37 "properties": { 37 38 "title": { 38 39 "type": "string", 39 - "description": "Thread title", 40 40 "maxLength": 200, 41 - "minLength": 1 41 + "minLength": 1, 42 + "description": "Thread title" 42 43 }, 43 44 "body": { 44 45 "type": "string", 45 - "description": "Thread body content", 46 - "maxLength": 10000 46 + "maxLength": 10000, 47 + "description": "Thread body content" 47 48 }, 48 49 "createdAt": { 49 50 "type": "string",
+2
website/content/docs/cli/06-generate.md
··· 340 340 **Input JSON:** 341 341 ```json 342 342 { 343 + "$type": "com.atproto.lexicon.schema", 343 344 "lexicon": 1, 344 345 "id": "com.example.thread", 345 346 "defs": { 346 347 "main": { 347 348 "type": "record", 349 + "key": "tid", 348 350 "record": { 349 351 "type": "object", 350 352 "required": ["title", "createdAt"],
+1
website/content/docs/language-guide/01-your-first-lexicon.md
··· 41 41 42 42 ```json 43 43 { 44 + "$type": "com.atproto.lexicon.schema", 44 45 "lexicon": 1, 45 46 "id": "com.example.forum.profile", 46 47 "defs": {
+3
website/content/docs/language-guide/10-important-info.md
··· 42 42 This generates: 43 43 ```json 44 44 { 45 + "$type": "com.atproto.lexicon.schema", 45 46 "lexicon": 1, 46 47 "id": "com.example.forum.post", 47 48 "defs": { ··· 72 73 73 74 ```json 74 75 { 76 + "$type": "com.atproto.lexicon.schema", 75 77 "lexicon": 1, 76 78 "id": "com.example.forum.post", 77 79 "defs": { ··· 105 107 Generates: 106 108 ```json 107 109 { 110 + "$type": "com.atproto.lexicon.schema", 108 111 "lexicon": 1, 109 112 "id": "com.example.forum.thread", 110 113 "defs": {
+15 -3
website/content/docs/language-guide/11-lexicon-mapping.md
··· 23 23 **Generated JSON:** 24 24 ```json 25 25 { 26 + "$type": "com.atproto.lexicon.schema", 26 27 "lexicon": 1, 27 28 "id": "com.example.forum.post", 28 29 "defs": { ··· 71 72 **Generated JSON:** 72 73 ```json 73 74 { 75 + "$type": "com.atproto.lexicon.schema", 74 76 "lexicon": 1, 75 77 "id": "com.example.forum.getPost", 76 78 "defs": { ··· 94 96 } 95 97 }, 96 98 "errors": [ 97 - { "name": "NotFound" }, 98 - { "name": "BadRequest" } 99 + { 100 + "name": "NotFound" 101 + }, 102 + { 103 + "name": "BadRequest" 104 + } 99 105 ] 100 106 } 101 107 } ··· 126 132 **Generated JSON:** 127 133 ```json 128 134 { 135 + "$type": "com.atproto.lexicon.schema", 129 136 "lexicon": 1, 130 137 "id": "com.example.forum.createPost", 131 138 "defs": { ··· 161 168 } 162 169 }, 163 170 "errors": [ 164 - { "name": "TextTooLong" } 171 + { 172 + "name": "TextTooLong" 173 + } 165 174 ] 166 175 } 167 176 } ··· 197 206 **Generated JSON:** 198 207 ```json 199 208 { 209 + "$type": "com.atproto.lexicon.schema", 200 210 "lexicon": 1, 201 211 "id": "com.example.subscribeEvents", 202 212 "defs": { ··· 266 276 **Generated JSON:** 267 277 ```json 268 278 { 279 + "$type": "com.atproto.lexicon.schema", 269 280 "lexicon": 1, 270 281 "id": "com.example.post", 271 282 "defs": { 272 283 "main": { 273 284 "type": "record", 285 + "key": "tid", 274 286 "record": { 275 287 "type": "object", 276 288 "properties": {