schematic/config/
path.rs

1use std::fmt::{self, Display};
2
3/// Represents all the different forms a path is composed of.
4#[derive(Clone, Debug)]
5pub enum PathSegment {
6    /// List index: `[0]`
7    Index(usize),
8    /// Map key: `name.`
9    Key(String),
10    /// Enum variant: `name.`
11    Variant(String),
12    /// Unknown segment: `?`
13    Unknown,
14}
15
16/// Represents the path from the configuration root to a nested field or field value.
17#[derive(Clone, Debug, Default)]
18pub struct Path {
19    /// List of path segments.
20    segments: Vec<PathSegment>,
21}
22
23impl Path {
24    /// Create a new instance with the provided [`PathSegment`]s.
25    pub fn new(segments: Vec<PathSegment>) -> Self {
26        Self { segments }
27    }
28
29    /// Create a new instance and append the provided [`PathSegment`]
30    /// to the end of the current path.
31    pub fn join(&self, segment: PathSegment) -> Self {
32        let mut path = self.clone();
33        path.segments.push(segment);
34        path
35    }
36
37    /// Create a new instance and append an `Index` [`PathSegment`]
38    /// to the end of the current path.
39    pub fn join_index(&self, index: usize) -> Self {
40        self.join(PathSegment::Index(index))
41    }
42
43    /// Create a new instance and append an `Key` [`PathSegment`]
44    /// to the end of the current path.
45    pub fn join_key(&self, key: impl Display) -> Self {
46        self.join(PathSegment::Key(format!("{key}")))
47    }
48
49    /// Create a new instance and append another [`Path`]
50    /// to the end of the current path.
51    pub fn join_path(&self, other: &Self) -> Self {
52        let mut path = self.clone();
53        path.segments.extend(other.segments.clone());
54        path
55    }
56
57    /// Create a new instance and append an `Variant` [`PathSegment`]
58    /// to the end of the current path.
59    pub fn join_variant(&self, variant: &str) -> Self {
60        self.join(PathSegment::Variant(variant.to_owned()))
61    }
62}
63
64impl Display for Path {
65    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
66        if self.segments.is_empty() {
67            return formatter.write_str(".");
68        }
69
70        let mut separator = "";
71
72        for segment in &self.segments {
73            match segment {
74                PathSegment::Index(index) => {
75                    write!(formatter, "[{index}]")?;
76                }
77                PathSegment::Key(key) | PathSegment::Variant(key) => {
78                    write!(formatter, "{separator}{key}")?;
79                }
80                PathSegment::Unknown => {
81                    write!(formatter, "{separator}?")?;
82                }
83            }
84
85            separator = ".";
86        }
87
88        Ok(())
89    }
90}