extern crate alloc;
use alloc::vec::Vec;
use super::Rhapsody;
use super::placement::{Dot, RawDot};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Verge {
Origin,
Before(RawDot),
After(RawDot),
Terminus,
}
impl Verge {
#[must_use]
pub const fn dot(self) -> Option<RawDot> {
match self {
Self::Origin | Self::Terminus => None,
Self::Before(dot) | Self::After(dot) => Some(dot),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Extent {
Covered(Vec<Dot>),
Inverted,
Dangling {
start: bool,
end: bool,
},
}
impl Extent {
#[must_use]
pub fn covered(&self) -> Option<&[Dot]> {
match self {
Self::Covered(dots) => Some(dots),
Self::Inverted | Self::Dangling { .. } => None,
}
}
}
impl Rhapsody {
#[must_use]
pub fn extent(&self, start: Verge, end: Verge) -> Extent {
let dangles = |verge: Verge| {
verge
.dot()
.is_some_and(|raw| !Dot::try_from(raw).is_ok_and(|dot| self.is_reachable(dot)))
};
let (start_dangles, end_dangles) = (dangles(start), dangles(end));
if start_dangles || end_dangles {
return Extent::Dangling {
start: start_dangles,
end: end_dangles,
};
}
if start == end {
return Extent::Covered(Vec::new());
}
let mut covered = Vec::new();
let mut walk = match start {
Verge::Origin => self.order_walk(),
Verge::Terminus => return Extent::Inverted,
Verge::After(raw) => match Dot::try_from(raw)
.ok()
.and_then(|dot| self.order_walk_after(dot))
{
Some(walk) => walk,
None => {
return Extent::Dangling {
start: true,
end: false,
};
}
},
Verge::Before(raw) => {
let ends_here = end == Verge::After(raw);
let start_dot = Dot::try_from(raw).ok();
if let Some(dot) = start_dot
&& self.is_visible(dot)
{
covered.push(dot);
}
if ends_here {
return Extent::Covered(covered);
}
match start_dot.and_then(|dot| self.order_walk_after(dot)) {
Some(walk) => walk,
None => {
return Extent::Dangling {
start: true,
end: false,
};
}
}
}
};
loop {
let Some((slot, visible)) = walk.next_slot() else {
return match end {
Verge::Terminus => Extent::Covered(covered),
_ => Extent::Inverted,
};
};
let raw_slot: RawDot = slot.into();
if end == Verge::Before(raw_slot) {
return Extent::Covered(covered);
}
if visible {
covered.push(slot);
}
if end == Verge::After(raw_slot) {
return Extent::Covered(covered);
}
}
}
}