use std::collections::BTreeSet;
use crate::attributes::Boundary;
use crate::document::Document;
use crate::ids::{ComponentId, NodeId};
use crate::tree::Component;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PrivateUnderExternal {
pub component: ComponentId,
pub ancestor: ComponentId,
}
#[derive(Debug, Default)]
pub struct Boundaries {
external: BTreeSet<ComponentId>,
conflicts: Vec<PrivateUnderExternal>,
}
impl Boundaries {
#[must_use]
pub fn of(document: &Document) -> Self {
let mut boundaries = Self::default();
for root in &document.components {
boundaries.visit(root, Above::default());
}
boundaries
}
#[must_use]
pub fn is_external(&self, component: ComponentId) -> bool {
self.external.contains(&component)
}
#[must_use]
pub fn is_external_node(&self, node: NodeId) -> bool {
matches!(node, NodeId::Component(component) if self.is_external(component))
}
#[must_use]
pub fn conflicts(&self) -> &[PrivateUnderExternal] {
&self.conflicts
}
fn visit(&mut self, component: &Component, above: Above) {
let external = match component.boundary {
Some(Boundary::External) => true,
Some(Boundary::Private) => {
if let (Some(component), Some(ancestor)) = (component.id, above.ancestor) {
self.conflicts.push(PrivateUnderExternal {
component,
ancestor,
});
}
false
}
None => above.external,
};
if external && let Some(id) = component.id {
self.external.insert(id);
}
let below = Above {
external,
ancestor: if external {
component.id.or(above.ancestor)
} else {
None
},
};
for child in &component.components {
self.visit(child, below);
}
}
}
#[derive(Clone, Copy, Default)]
struct Above {
external: bool,
ancestor: Option<ComponentId>,
}