154 lines
5.4 KiB
Rust
154 lines
5.4 KiB
Rust
use crate::database::schema::Schema;
|
|
|
|
impl Schema {
|
|
pub fn compile_polymorphism(
|
|
&self,
|
|
db: &crate::database::Database,
|
|
root_id: &str,
|
|
path: &str,
|
|
errors: &mut Vec<crate::drop::Error>,
|
|
) {
|
|
let mut options = std::collections::BTreeMap::new();
|
|
let mut strategy = String::new();
|
|
|
|
if let Some(family) = &self.obj.family {
|
|
let family_base = family.split('.').next_back().unwrap_or(family).to_string();
|
|
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.get_scoped_schema(crate::database::realm::SchemaRealm::Type, &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);
|
|
}
|
|
}
|
|
}
|