use super::{RegularType, SchemaMut};
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.type_, 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")]
#[non_exhaustive]
pub struct UnconditionalCycle {}
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].type_ {
RegularType::Record(record) => &record.fields,
_ => unreachable!(),
} {
if let RegularType::Record(_) = &schema.nodes[field.type_.idx].type_ {
if visited_nodes[field.type_.idx] {
return Err(UnconditionalCycle {});
} 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(())
}