stems fixes and tests

This commit is contained in:
2026-03-13 01:17:27 -04:00
parent 797a0a5460
commit 6a275e1d90
9 changed files with 304 additions and 115 deletions

View File

@ -265,6 +265,7 @@ impl Database {
String::from(""),
None,
None,
true,
&mut inner_map,
&mut errors,
);
@ -290,21 +291,16 @@ impl Database {
mut current_path: String,
parent_type: Option<String>,
property_name: Option<String>,
is_root: bool,
inner_map: &mut HashMap<String, Arc<Stem>>,
errors: &mut Vec<crate::drop::Error>,
) {
let mut is_entity = false;
let mut entity_type = String::new();
let mut examine_id = None;
if let Some(ref r) = schema.obj.r#ref {
examine_id = Some(r.clone());
} else if let Some(ref id) = schema.obj.id {
examine_id = Some(id.clone());
}
if let Some(target) = examine_id {
let parts: Vec<&str> = target.split('.').collect();
// First check if the Schema's $id is a native Database Type
if let Some(ref id) = schema.obj.id {
let parts: Vec<&str> = id.split('.').collect();
if let Some(last_seg) = parts.last() {
if db.types.contains_key(*last_seg) {
is_entity = true;
@ -313,6 +309,20 @@ impl Database {
}
}
// If not found via $id, check the $ref pointer
// This allows ad-hoc schemas (like `save_person.response`) to successfully adopt the Type of what they $ref
if !is_entity {
if let Some(ref r) = schema.obj.r#ref {
let parts: Vec<&str> = r.split('.').collect();
if let Some(last_seg) = parts.last() {
if db.types.contains_key(*last_seg) {
is_entity = true;
entity_type = last_seg.to_string();
}
}
}
}
let mut relation_col = None;
if is_entity {
if let (Some(pt), Some(prop)) = (&parent_type, &property_name) {
@ -340,22 +350,37 @@ impl Database {
schema: Arc::new(schema.clone()),
};
let mut branch_path = current_path.clone();
if !current_path.is_empty() {
branch_path = format!("{}/{}", current_path, entity_type);
}
let mut branch_path = if is_root {
String::new()
} else if current_path.is_empty() {
entity_type.clone()
} else {
format!("{}/{}", current_path, entity_type)
};
if inner_map.contains_key(&branch_path) {
errors.push(crate::drop::Error {
code: "STEM_COLLISION".to_string(),
message: format!("The stem path `{}` resolves to multiple Entity boundaries. This usually occurs during un-wrapped $family or oneOf polymorphic schemas where multiple Entities are directly assigned to the same property. To fix this, encapsulate the polymorphic branch.", branch_path),
details: crate::drop::ErrorDetails {
path: root_schema_id.to_string(),
},
});
}
// DEDUPLICATION: If we just recursed into the EXACT same entity type definition,
// do not append again and do not re-register the stem.
let already_registered =
if current_path == entity_type || current_path.ends_with(&format!("/{}", entity_type)) {
branch_path = current_path.clone();
true
} else {
false
};
inner_map.insert(branch_path.clone(), Arc::new(stem));
if !already_registered {
if inner_map.contains_key(&branch_path) {
errors.push(crate::drop::Error {
code: "STEM_COLLISION".to_string(),
message: format!("The stem path `{}` resolves to multiple Entity boundaries. This usually occurs during un-wrapped $family or oneOf polymorphic schemas where multiple Entities are directly assigned to the same property. To fix this, encapsulate the polymorphic branch.", branch_path),
details: crate::drop::ErrorDetails {
path: root_schema_id.to_string(),
},
});
}
inner_map.insert(branch_path.clone(), Arc::new(stem));
}
// Update current_path for structural children
current_path = branch_path;
@ -381,6 +406,7 @@ impl Database {
current_path.clone(),
next_parent.clone(),
Some(k.clone()),
false,
inner_map,
errors,
);
@ -403,6 +429,7 @@ impl Database {
next_path,
next_parent.clone(),
Some(k.clone()),
false,
inner_map,
errors,
);
@ -418,11 +445,32 @@ impl Database {
current_path.clone(),
next_parent.clone(),
property_name.clone(),
false, // Arrays themselves aren't polymorphic branches, their items might be
inner_map,
errors,
);
}
// Follow external reference if we didn't just crawl local properties
if schema.obj.properties.is_none() && schema.obj.items.is_none() && schema.obj.one_of.is_none()
{
if let Some(ref r) = schema.obj.r#ref {
if let Some(target_schema) = db.schemas.get(r) {
Self::discover_stems(
db,
root_schema_id,
target_schema,
current_path.clone(),
next_parent.clone(),
property_name.clone(),
false,
inner_map,
errors,
);
}
}
}
// Polymorphism branch
if let Some(arr) = &schema.obj.one_of {
for v in arr {
@ -433,6 +481,7 @@ impl Database {
current_path.clone(),
next_parent.clone(),
property_name.clone(),
false,
inner_map,
errors,
);
@ -447,6 +496,7 @@ impl Database {
current_path.clone(),
next_parent.clone(),
property_name.clone(),
false,
inner_map,
errors,
);