filter checkpoint
This commit is contained in:
153
src/database/compile/polymorphism.rs
Normal file
153
src/database/compile/polymorphism.rs
Normal file
@ -0,0 +1,153 @@
|
||||
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.schemas.contains_key(&target_id) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user