1use std::fmt::Display;
5
6use smallvec::SmallVec;
7
8use crate::Edge;
9
10pub type PathEdges = SmallVec<[Edge; 16]>;
15
16#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
19pub enum Path {
20 Arc(PathEdges),
21 Circ(PathEdges),
22}
23
24impl Path {
25 pub fn arc<I>(edges: I) -> Self
26 where I: IntoIterator<Item = Edge> {
27 let edges: PathEdges = edges.into_iter().collect();
28 assert!(!edges.is_empty());
29 Self::Arc(edges)
30 }
31
32 pub fn circ<I>(edges: I) -> Self
33 where I: IntoIterator<Item = Edge> {
34 let edges: PathEdges = edges.into_iter().collect();
35 assert!(!edges.is_empty());
36 Self::Circ(edges)
37 }
38
39 pub fn is_arc(&self) -> bool { matches!(self, Self::Arc(_)) }
40 pub fn is_circle(&self) -> bool { matches!(self, Self::Circ(_)) }
41
42 pub fn contains(&self, e: Edge) -> bool {
43 match self {
44 Self::Arc(es) | Self::Circ(es) => es.contains(&e),
45 }
46 }
47
48 pub fn len(&self) -> usize {
49 match self {
50 Self::Arc(es) | Self::Circ(es) => es.len(),
51 }
52 }
53
54 pub fn edges(&self) -> &[Edge] {
55 match self {
56 Self::Arc(es) | Self::Circ(es) => &es[..],
57 }
58 }
59
60 pub fn min_edge(&self) -> Edge {
61 match self {
62 Self::Arc(es) | Self::Circ(es) => *es.iter().min().unwrap(),
63 }
64 }
65
66 pub fn end_pts(&self) -> Option<(Edge, Edge)> {
67 match self {
68 Self::Arc(es) => Some((es[0], *es.last().unwrap())),
69 Self::Circ(_) => None,
70 }
71 }
72
73 pub fn into_seq(self) -> PathEdges {
74 match self {
75 Self::Arc(es) | Self::Circ(es) => es,
76 }
77 }
78}
79
80impl Display for Path {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 let c = self.edges().iter().map(|e| e.to_string()).collect::<Vec<_>>().join("-");
83 if self.is_circle() {
84 write!(f, "⚪︎({c})")
85 } else {
86 write!(f, "[{c}]")
87 }
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn arc_and_circ() {
97 let a = Path::arc([1, 2, 3]);
98 assert!(a.is_arc());
99 assert!(!a.is_circle());
100 assert_eq!(a.len(), 3);
101 assert_eq!(a.edges(), &[1, 2, 3]);
102
103 let c = Path::circ([1, 2, 3]);
104 assert!(c.is_circle());
105 assert!(!c.is_arc());
106 assert_eq!(c.edges(), a.edges());
107
108 assert_ne!(a, c);
110 }
111
112 #[test]
113 #[should_panic]
114 fn arc_rejects_empty() {
115 let _ = Path::arc([]);
116 }
117
118 #[test]
119 #[should_panic]
120 fn circ_rejects_empty() {
121 let _ = Path::circ([]);
122 }
123
124 #[test]
125 fn contains_and_min_edge() {
126 let p = Path::arc([4, 2, 7]);
127 assert!(p.contains(2));
128 assert!(!p.contains(3));
129 assert_eq!(p.min_edge(), 2);
130 }
131
132 #[test]
133 fn end_pts_only_for_arcs() {
134 assert_eq!(Path::arc([4, 2, 7]).end_pts(), Some((4, 7)));
135 assert_eq!(Path::arc([5]).end_pts(), Some((5, 5)));
136 assert_eq!(Path::circ([4, 2, 7]).end_pts(), None);
137 }
138
139 #[test]
140 fn equality_is_oriented() {
141 let p = Path::circ([1, 2, 3]);
143 assert_ne!(p, Path::circ([3, 2, 1]));
144 assert_ne!(p, Path::circ([2, 3, 1]));
145 }
146
147 #[test]
148 fn into_seq_keeps_the_order() {
149 assert_eq!(Path::circ([4, 2, 7]).into_seq().to_vec(), vec![4, 2, 7]);
150 }
151
152 #[test]
153 fn display() {
154 assert_eq!(Path::arc([1, 2, 3]).to_string(), "[1-2-3]");
155 assert_eq!(Path::circ([1, 2, 3]).to_string(), "⚪︎(1-2-3)");
156 }
157}