use super::{RegularType, SchemaMut, SchemaNode};
impl SchemaMut {
pub(crate) fn check_for_cycles(&self) -> Result<(), UnconditionalCycle> {
let mut visited_nodes = vec![false; self.nodes.len()];
let mut checked_nodes = vec![false; self.nodes.len()];
for (idx, node) in self.nodes.iter().enumerate() {
if matches!(node, SchemaNode::RegularType(RegularType::Record(_)))
&& !checked_nodes[idx]
{
check_no_zero_sized_cycle_inner(self, idx, &mut visited_nodes, &mut checked_nodes)?;
}
}
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
#[error("The schema contains a record that ends up always containing itself")]
pub struct UnconditionalCycle {
_private: (),
}
fn check_no_zero_sized_cycle_inner(
schema: &SchemaMut,
node_idx: usize,
visited_nodes: &mut Vec<bool>,
checked_nodes: &mut Vec<bool>,
) -> Result<(), UnconditionalCycle> {
visited_nodes[node_idx] = true;
for field in match &schema.nodes[node_idx] {
SchemaNode::RegularType(RegularType::Record(record)) => &record.fields,
_ => unreachable!(),
} {
if let SchemaNode::RegularType(RegularType::Record(_)) = &schema.nodes[field.type_.idx] {
if visited_nodes[field.type_.idx] {
return Err(UnconditionalCycle { _private: () });
} else {
check_no_zero_sized_cycle_inner(
schema,
field.type_.idx,
visited_nodes,
checked_nodes,
)?;
}
}
}
visited_nodes[node_idx] = false;
checked_nodes[node_idx] = true;
Ok(())
}