use super::*;
pub(super) fn validate_makearray_regions_in_registries(
registries: &std::collections::HashMap<String, CompRegistry>,
) -> Result<(), ExpressionTemplateError> {
for reg in registries.values() {
for (tname, decl) in ®.named {
let body = decl_body(decl);
if matches!(&*body, SNode::Null) {
continue;
}
validate_makearray_regions(
&to_value(&body),
&format!("expression_templates.{tname}/body"),
)?;
}
}
Ok(())
}
fn template_manifold_bearing(named: &Named) -> std::collections::HashMap<String, bool> {
fn direct(node: &Sv, seen: &mut PtrSet) -> bool {
match &**node {
SNode::Arr(items) => {
if !seen.insert(node) {
return false;
}
items.iter().any(|c| direct(c, seen))
}
SNode::Obj(fields) => {
if !seen.insert(node) {
return false;
}
if let Some(op) = obj_op(fields)
&& GEOMETRY_MANIFOLD_OPS.contains(&op)
{
return true;
}
fields.iter().any(|(_, v)| direct(v, seen))
}
_ => false,
}
}
transitive_reachable(named, |body| direct(body, &mut PtrSet::default()))
}
pub(super) fn validate_geometry_manifolds_refaware(
value: &Value,
registries: &std::collections::HashMap<String, CompRegistry>,
) -> Result<(), ExpressionTemplateError> {
validate_geometry_manifolds(value, "")?;
let Some(root) = value.as_object() else {
return Ok(());
};
for compkind in ["models", "reaction_systems"] {
let Some(comps) = root.get(compkind).and_then(|v| v.as_object()) else {
continue;
};
for (cname, comp) in comps {
let Some(comp_obj) = comp.as_object() else {
continue;
};
let Some(reg) = registries.get(cname) else {
continue;
};
let manifold_bearing = template_manifold_bearing(®.named);
if !manifold_bearing.values().any(|b| *b) {
continue; }
let mut memo = PtrSet::default();
for (k, v) in comp_obj {
if k == "expression_templates" {
continue;
}
if k == "equations"
&& let Some(items) = v.as_array()
{
for (i, eq) in items.iter().enumerate() {
let label = eq
.get("lhs")
.and_then(Value::as_str)
.map(str::to_string)
.unwrap_or_else(|| i.to_string());
let shared = to_shared(eq);
validate_manifolds_in_refs(
&shared,
®.named,
&manifold_bearing,
&format!("{compkind}.{cname}.equations/{label}"),
&mut memo,
)?;
}
continue;
}
let shared = to_shared(v);
validate_manifolds_in_refs(
&shared,
®.named,
&manifold_bearing,
&format!("{compkind}.{cname}.{k}"),
&mut memo,
)?;
}
}
}
Ok(())
}
fn validate_manifolds_in_refs(
node: &Sv,
named: &Named,
manifold_bearing: &std::collections::HashMap<String, bool>,
path: &str,
memo: &mut PtrSet,
) -> Result<(), ExpressionTemplateError> {
match &**node {
SNode::Arr(items) => {
if !memo.insert(node) {
return Ok(());
}
for (i, c) in items.iter().enumerate() {
validate_manifolds_in_refs(
c,
named,
manifold_bearing,
&format!("{path}/{i}"),
memo,
)?;
}
}
SNode::Obj(fields) => {
if !memo.insert(node) {
return Ok(());
}
let name = if obj_op(fields) == Some(APPLY_OP) {
match obj_get(fields, "name").map(|v| &**v) {
Some(SNode::Str(s)) => s.as_str(),
_ => "",
}
} else {
""
};
if !name.is_empty() && manifold_bearing.get(name).copied().unwrap_or(false) {
let mut expand_memo = PtrMemo::default();
if let Ok(expansion) = expand_all(node, named, path, &mut expand_memo) {
let ev = to_value(&expansion);
if let Err(e) = validate_geometry_manifolds(&ev, "") {
if e.code == codes::GEOMETRY_MANIFOLD_INVALID {
return Err(err(
codes::GEOMETRY_MANIFOLD_INVALID,
format!(
"{path}: instantiation of template '{name}' — {} \
(esm-spec §9.6.9; per-instantiation manifold check)",
e.message
),
));
}
return Err(e);
}
}
}
for (k, v) in fields {
validate_manifolds_in_refs(
v,
named,
manifold_bearing,
&format!("{path}/{k}"),
memo,
)?;
}
}
_ => {}
}
Ok(())
}