config/
path.rs

1const EMPTY: &str = "";
2const KEY_DELIMITER: &str = ":";
3
4/// Represents a configuration path.
5#[derive(Copy, Clone, Debug, PartialEq)]
6pub enum ConfigurationPath {
7    /// Indicates a qualified, absolute configuration path.
8    Absolute,
9
10    // Indicates a configuration path relative to a parent.
11    Relative
12}
13
14impl ConfigurationPath {
15    /// Gets the key delimiter used in configuration paths.
16    pub fn key_delimiter() -> &'static str {
17        KEY_DELIMITER
18    }
19
20    /// Combines the specified segments into one path.
21    ///
22    /// # Arguments
23    ///
24    /// * `segments` - The segments to combine into a path
25    pub fn combine(segments: &[&str]) -> String {
26        segments.join(KEY_DELIMITER)
27    }
28
29    /// Extracts the last path segment from the path.
30    ///
31    /// # Arguments
32    ///
33    /// * `path` - The path to extract the key from
34    pub fn section_key(path: &str) -> &str {
35        if let Some(index) = path.rfind(KEY_DELIMITER) {
36            &path[(index + 1)..]
37        } else {
38            path
39        }
40    }
41
42    /// Extracts the path corresponding to the parent node for a given path.
43    ///
44    /// # Arguments
45    ///
46    /// * `path` - The path to extract the parent path from
47    pub fn parent_path(path: &str) -> &str {
48        if let Some(index) = path.rfind(KEY_DELIMITER) {
49            &path[..index]
50        } else {
51            EMPTY
52        }
53    }
54}
55
56#[cfg(test)]
57mod tests {
58
59    use super::*;
60    use test_case::test_case;
61
62    #[test_case(&["parent", ""], "parent:" ; "with 1 segment")]
63    #[test_case(&["parent", "", ""], "parent::" ; "with 2 segments")]
64    #[test_case(&["parent", "", "", "key"], "parent:::key" ; "with segments in between")]
65    fn combine_with_empty_segment_leaves_delimiter(segments: &[&str], expected: &str) {
66        // arrange
67
68        // act
69        let path = ConfigurationPath::combine(segments);
70
71        // assert
72        assert_eq!(&path, expected);
73    }
74
75    #[test_case("", "" ; "when empty")]
76    #[test_case(":::", "" ; "when only delimiters")]
77    #[test_case("a::b:::c", "c" ; "with empty segments in the middle")]
78    #[test_case("a:::b:", "" ; "when last segment is empty")]
79    #[test_case("key", "key" ; "with no parent")]
80    #[test_case(":key", "key" ; "with 1 empty parent")]
81    #[test_case("::key", "key" ; "with 2 empty parents")]
82    #[test_case("parent:key", "key" ; "with parent")]
83    fn section_key_should_return_expected_segment(path: &str, expected: &str) {
84        // arrange
85
86        // act
87        let key = ConfigurationPath::section_key(path);
88
89        // assert
90        assert_eq!(key, expected);
91    }
92
93    #[test_case("", "" ; "when empty")]
94    #[test_case(":::", "::" ; "when only delimiters")]
95    #[test_case("a::b:::c", "a::b::" ; "with empty segments in the middle")]
96    #[test_case("a:::b:", "a:::b" ; "when last segment is empty")]
97    #[test_case("key", "" ; "with no parent")]
98    #[test_case(":key", "" ; "with 1 empty parent")]
99    #[test_case("::key", ":" ; "with 2 empty parents")]
100    #[test_case("parent:key", "parent" ; "with parent")]
101    fn parent_path_should_return_expected_segment(path: &str, expected: &str) {
102        // arrange
103
104        // act
105        let key = ConfigurationPath::parent_path(path);
106
107        // assert
108        assert_eq!(key, expected);
109    }
110}