Skip to content

Commit 849a4fe

Browse files
feat: wire schema_extensions from config file and preserve union keyword during merge
The merge function was converting anyOf→oneOf which broke discriminator auto-detection, generating untagged enums instead of internally tagged. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2b0d233 commit 849a4fe

3 files changed

Lines changed: 32 additions & 8 deletions

File tree

src/analysis.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -393,17 +393,26 @@ fn merge_json_objects_with_rules(
393393
match (main, extension) {
394394
// Both objects - merge properties
395395
(Value::Object(mut main_obj), Value::Object(ext_obj)) => {
396-
// Special handling for schema objects with oneOf/anyOf variants
396+
// Special handling for schema objects with oneOf/anyOf variants.
397+
// Detect which keyword the MAIN spec uses so we preserve it after merging.
398+
let main_union_keyword = if main_obj.contains_key("oneOf") {
399+
Some("oneOf")
400+
} else if main_obj.contains_key("anyOf") {
401+
Some("anyOf")
402+
} else {
403+
None
404+
};
397405
if let (Some(main_variants), Some(ext_variants)) = (
398406
extract_schema_variants(&Value::Object(main_obj.clone())),
399407
extract_schema_variants(&Value::Object(ext_obj.clone())),
400408
) {
409+
let union_key = main_union_keyword.unwrap_or("oneOf");
401410
println!(
402-
"🔍 Merging union schemas: {} main variants, {} extension variants",
411+
"🔍 Merging union schemas ({union_key}): {} main variants, {} extension variants",
403412
main_variants.len(),
404413
ext_variants.len()
405414
);
406-
// Merge the variant arrays and use oneOf as the canonical key
415+
// Merge the variant arrays, preserving the original union keyword
407416
// First, collect main variants, but filter out any that will be replaced by extension
408417
let mut merged_variants = Vec::new();
409418
let extension_refs: Vec<String> = ext_variants
@@ -438,10 +447,10 @@ fn merge_json_objects_with_rules(
438447
merged_variants.push(ext_variant);
439448
}
440449

441-
// Remove old oneOf/anyOf keys and add the merged oneOf
450+
// Remove old oneOf/anyOf keys and add merged variants under the original keyword
442451
main_obj.remove("oneOf");
443452
main_obj.remove("anyOf");
444-
main_obj.insert("oneOf".to_string(), Value::Array(merged_variants));
453+
main_obj.insert(union_key.to_string(), Value::Array(merged_variants));
445454

446455
// Merge other properties normally
447456
for (key, ext_value) in ext_obj {

src/bin/openapi-to-rust.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
7878
serde_json::from_str(&spec_content)?
7979
};
8080

81-
// Analyze schemas
81+
// Analyze schemas (with extensions if configured)
8282
println!("🔍 Analyzing schemas...");
83-
let mut analyzer = SchemaAnalyzer::new(spec_value)?;
83+
let mut analyzer = if generator_config.schema_extensions.is_empty() {
84+
SchemaAnalyzer::new(spec_value)?
85+
} else {
86+
println!(
87+
"📎 Merging {} schema extension(s)",
88+
generator_config.schema_extensions.len()
89+
);
90+
SchemaAnalyzer::new_with_extensions(
91+
spec_value,
92+
&generator_config.schema_extensions,
93+
)?
94+
};
8495
let mut analysis = analyzer.analyze()?;
8596

8697
println!("📊 Found {} schemas", analysis.schemas.len());

src/config.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ pub struct GeneratorSection {
169169
pub output_dir: PathBuf,
170170
#[validate(length(min = 1, message = "module_name cannot be empty"))]
171171
pub module_name: String,
172+
/// Schema extension files to merge into the main spec before codegen.
173+
/// Paths are relative to the working directory (same as spec_path).
174+
#[serde(default)]
175+
pub schema_extensions: Vec<PathBuf>,
172176
}
173177

174178
#[derive(Debug, Clone, Deserialize, Serialize, Validate)]
@@ -533,7 +537,7 @@ impl ConfigFile {
533537
},
534538
streaming_config,
535539
nullable_field_overrides: self.nullable_overrides,
536-
schema_extensions: vec![],
540+
schema_extensions: self.generator.schema_extensions,
537541
http_client_config,
538542
retry_config,
539543
tracing_enabled,

0 commit comments

Comments
 (0)