use crate::database::schema::Schema; impl Schema { pub fn compile_polymorphism( &self, db: &crate::database::Database, root_id: &str, path: &str, errors: &mut Vec, ) { let mut options = std::collections::BTreeMap::new(); let mut strategy = String::new(); if let Some(family) = &self.obj.family { // Formalize the . topology // family_base extracts the 'Base' (e.g. 'widget', 'person') let family_base = family.split('.').next_back().unwrap_or(family).to_string(); // family_prefix extracts the 'Variant' (e.g. 'stock', 'light') let family_prefix = family .strip_suffix(&family_base) .unwrap_or("") .trim_end_matches('.'); if let Some(type_def) = db.types.get(&family_base) { if type_def.variations.len() > 1 && type_def.variations.iter().any(|v| v != &family_base) { // Scenario A / B: Table Variations strategy = "type".to_string(); for var in &type_def.variations { let target_id = if family_prefix.is_empty() { var.to_string() } else { format!("{}.{}", family_prefix, var) }; if db.schemas.get(&target_id).is_some() { options.insert(var.to_string(), (None, Some(target_id))); } } } else { // Scenario C: Single Table Inheritance (Horizontal) strategy = "kind".to_string(); let suffix = format!(".{}", family_base); for (id, schema) in &type_def.schemas { if id.ends_with(&suffix) || id == &family_base { if let Some(kind_val) = schema.obj.get_discriminator_value("kind", id) { options.insert(kind_val, (None, Some(id.to_string()))); } } } } } } else if let Some(one_of) = &self.obj.one_of { let mut type_vals = std::collections::HashSet::new(); let mut kind_vals = std::collections::HashSet::new(); let mut disjoint_base = true; let mut structural_types = std::collections::HashSet::new(); for c in one_of { let mut child_id = String::new(); let mut child_is_primitive = false; if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ { if crate::database::object::is_primitive_type(t) { child_is_primitive = true; structural_types.insert(t.clone()); } else { child_id = t.clone(); structural_types.insert("object".to_string()); } } else { disjoint_base = false; } if !child_is_primitive { if let Some(t_val) = c.obj.get_discriminator_value("type", &child_id) { type_vals.insert(t_val); } if let Some(k_val) = c.obj.get_discriminator_value("kind", &child_id) { kind_vals.insert(k_val); } } } if disjoint_base && structural_types.len() == one_of.len() { strategy = "".to_string(); for (i, c) in one_of.iter().enumerate() { if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ { if crate::database::object::is_primitive_type(t) { options.insert(t.clone(), (Some(i), None)); } else { options.insert("object".to_string(), (Some(i), None)); } } } } else { strategy = if type_vals.len() > 1 && type_vals.len() == one_of.len() { "type".to_string() } else if kind_vals.len() > 1 && kind_vals.len() == one_of.len() { "kind".to_string() } else { "".to_string() }; if strategy.is_empty() { errors.push(crate::drop::Error { code: "AMBIGUOUS_POLYMORPHISM".to_string(), message: format!("oneOf boundaries must map mathematically unique 'type' or 'kind' discriminators, or strictly contain disjoint primitive types."), details: crate::drop::ErrorDetails { path: Some(path.to_string()), schema: Some(root_id.to_string()), ..Default::default() } }); return; } for (i, c) in one_of.iter().enumerate() { let mut child_id = String::new(); if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ { if !crate::database::object::is_primitive_type(t) { child_id = t.clone(); } } if let Some(val) = c.obj.get_discriminator_value(&strategy, &child_id) { if options.contains_key(&val) { errors.push(crate::drop::Error { code: "POLYMORPHIC_COLLISION".to_string(), message: format!("Polymorphic boundary defines multiple candidates mapped to the identical discriminator value '{}'.", val), details: crate::drop::ErrorDetails { path: Some(path.to_string()), schema: Some(root_id.to_string()), ..Default::default() } }); continue; } options.insert(val, (Some(i), None)); } } } } else { return; } if !options.is_empty() { if !strategy.is_empty() { let _ = self.obj.compiled_discriminator.set(strategy); } let _ = self.obj.compiled_options.set(options); } } }