1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct RdPath {
9 segments: Vec<RdPathSegment>,
10}
11
12impl RdPath {
13 pub fn new(segments: Vec<RdPathSegment>) -> Self {
14 Self { segments }
15 }
16
17 pub fn segments(&self) -> &[RdPathSegment] {
18 &self.segments
19 }
20
21 pub fn with_child(&self, index: usize) -> RdPath {
22 let mut path = self.clone();
23 path.segments.push(RdPathSegment::Child(index));
24 path
25 }
26
27 pub fn with_option(&self) -> RdPath {
28 let mut path = self.clone();
29 path.segments.push(RdPathSegment::Option);
30 path
31 }
32
33 pub(crate) fn with_character(&self, index: usize) -> RdPath {
34 let mut path = self.clone();
35 path.segments.push(RdPathSegment::CharacterElement(index));
36 path
37 }
38}
39
40impl From<Vec<RdPathSegment>> for RdPath {
41 fn from(segments: Vec<RdPathSegment>) -> Self {
42 Self::new(segments)
43 }
44}
45
46impl fmt::Display for RdPath {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 for (index, segment) in self.segments.iter().enumerate() {
49 if index != 0 {
50 f.write_str(" / ")?;
51 }
52 segment.fmt(f)?;
53 }
54 Ok(())
55 }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
59#[non_exhaustive]
60pub enum RdPathSegment {
61 TopLevel(usize),
62 Child(usize),
63 Option,
64 Attribute(String),
65 AttributeValue,
66 ListElement(usize),
67 CharacterElement(usize),
68}
69
70impl fmt::Display for RdPathSegment {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match self {
73 Self::TopLevel(index) => write!(f, "top-level[{index}]"),
74 Self::Child(index) => write!(f, "child[{index}]"),
75 Self::Option => f.write_str("@option"),
76 Self::Attribute(name) => write!(f, "@attr({name})"),
77 Self::AttributeValue => f.write_str("value"),
78 Self::ListElement(index) => write!(f, "list[{index}]"),
79 Self::CharacterElement(index) => write!(f, "character[{index}]"),
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn displays_structural_paths() {
90 let path = RdPath::new(vec![
91 RdPathSegment::TopLevel(8),
92 RdPathSegment::Child(3),
93 RdPathSegment::Option,
94 RdPathSegment::Attribute("srcref".to_string()),
95 RdPathSegment::AttributeValue,
96 RdPathSegment::ListElement(2),
97 RdPathSegment::CharacterElement(0),
98 ]);
99 assert_eq!(
100 path.to_string(),
101 "top-level[8] / child[3] / @option / @attr(srcref) / value / list[2] / character[0]"
102 );
103 }
104}