Skip to main content

toml_scaffold/
field_path.rs

1/// A field path represented as a sequence of field names.
2///
3/// This struct is used to represent paths to fields in nested structures,
4/// where each segment is a field name.
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6pub struct FieldPath(Vec<String>);
7
8impl FieldPath {
9    /// Creates an empty field path.
10    pub fn new() -> Self {
11        Self(Vec::new())
12    }
13
14    /// Creates a field path from a vector of segments.
15    pub fn from_vec(segments: Vec<String>) -> Self {
16        Self(segments)
17    }
18
19    /// Appends a segment to the end of this path.
20    pub fn push(&mut self, segment: String) {
21        self.0.push(segment);
22    }
23
24    /// Creates a new path by appending a segment to this path.
25    pub fn child(&self, segment: String) -> Self {
26        let mut path = self.clone();
27        path.push(segment);
28        path
29    }
30
31    /// Returns the number of segments in this path.
32    pub fn len(&self) -> usize {
33        self.0.len()
34    }
35
36    /// Checks if this path starts with another path.
37    pub fn starts_with(&self, other: &FieldPath) -> bool {
38        self.0.starts_with(&other.0)
39    }
40
41    /// Returns the segment at the given index.
42    pub fn get(&self, index: usize) -> Option<&String> {
43        self.0.get(index)
44    }
45
46    /// Converts this path to a TOML dotted key string.
47    pub fn as_dotted_key(&self) -> String {
48        use toml_writer::TomlWrite;
49
50        let mut result = String::new();
51        for (i, segment) in self.0.iter().enumerate() {
52            if i > 0 {
53                result.push('.');
54            }
55            let _ = result.key(segment.as_str());
56        }
57        result
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use std::collections::HashMap;
65
66    #[test]
67    fn test_new() {
68        let path = FieldPath::new();
69        assert_eq!(path.len(), 0);
70        assert_eq!(path.as_dotted_key(), "");
71    }
72
73    #[test]
74    fn test_from_vec() {
75        let path = FieldPath::from_vec(vec!["a".to_string(), "b".to_string()]);
76        assert_eq!(path.len(), 2);
77        assert_eq!(path.as_dotted_key(), "a.b");
78    }
79
80    #[test]
81    fn test_push() {
82        let mut path = FieldPath::new();
83        path.push("first".to_string());
84        path.push("second".to_string());
85        assert_eq!(path.len(), 2);
86        assert_eq!(path.as_dotted_key(), "first.second");
87    }
88
89    #[test]
90    fn test_child() {
91        let parent = FieldPath::from_vec(vec!["parent".to_string()]);
92        let child = parent.child("child".to_string());
93        assert_eq!(parent.len(), 1);
94        assert_eq!(child.len(), 2);
95        assert_eq!(child.as_dotted_key(), "parent.child");
96    }
97
98    #[test]
99    fn test_starts_with() {
100        let parent = FieldPath::from_vec(vec!["a".to_string(), "b".to_string()]);
101        let child = FieldPath::from_vec(vec!["a".to_string(), "b".to_string(), "c".to_string()]);
102        let other = FieldPath::from_vec(vec!["x".to_string()]);
103
104        assert!(child.starts_with(&parent));
105        assert!(!child.starts_with(&other));
106        assert!(parent.starts_with(&parent));
107    }
108
109    #[test]
110    fn test_get() {
111        let path = FieldPath::from_vec(vec!["a".to_string(), "b".to_string(), "c".to_string()]);
112        assert_eq!(path.get(0), Some(&"a".to_string()));
113        assert_eq!(path.get(1), Some(&"b".to_string()));
114        assert_eq!(path.get(2), Some(&"c".to_string()));
115        assert_eq!(path.get(3), None);
116    }
117
118    #[test]
119    fn test_field_with_dots() {
120        let path = FieldPath::from_vec(vec!["field.with.dots".to_string()]);
121        assert_eq!(path.len(), 1);
122        assert_eq!(path.as_dotted_key(), "\"field.with.dots\"");
123    }
124
125    #[test]
126    fn test_hash_map_key() {
127        let mut map = HashMap::new();
128        let path1 = FieldPath::from_vec(vec!["a".to_string(), "b".to_string()]);
129        let path2 = FieldPath::from_vec(vec!["a".to_string(), "b".to_string()]);
130        let path3 = FieldPath::from_vec(vec!["a".to_string(), "c".to_string()]);
131
132        map.insert(path1.clone(), "value1");
133        map.insert(path3, "value2");
134
135        assert_eq!(map.get(&path2), Some(&"value1"));
136        assert_eq!(map.len(), 2);
137    }
138}