more-config 2.1.5

Provides support for configuration
Documentation
const EMPTY: &str = "";
const KEY_DELIMITER: &str = ":";

/// Represents a configuration path.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ConfigurationPath {
    /// Indicates a qualified, absolute configuration path.
    Absolute,

    // Indicates a configuration path relative to a parent.
    Relative
}

impl ConfigurationPath {
    /// Gets the key delimiter used in configuration paths.
    pub fn key_delimiter() -> &'static str {
        KEY_DELIMITER
    }

    /// Combines the specified segments into one path.
    ///
    /// # Arguments
    ///
    /// * `segments` - The segments to combine into a path
    pub fn combine(segments: &[&str]) -> String {
        segments.join(KEY_DELIMITER)
    }

    /// Extracts the last path segment from the path.
    ///
    /// # Arguments
    ///
    /// * `path` - The path to extract the key from
    pub fn section_key(path: &str) -> &str {
        if let Some(index) = path.rfind(KEY_DELIMITER) {
            &path[(index + 1)..]
        } else {
            path
        }
    }

    /// Extracts the path corresponding to the parent node for a given path.
    ///
    /// # Arguments
    ///
    /// * `path` - The path to extract the parent path from
    pub fn parent_path(path: &str) -> &str {
        if let Some(index) = path.rfind(KEY_DELIMITER) {
            &path[..index]
        } else {
            EMPTY
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use test_case::test_case;

    #[test_case(&["parent", ""], "parent:" ; "with 1 segment")]
    #[test_case(&["parent", "", ""], "parent::" ; "with 2 segments")]
    #[test_case(&["parent", "", "", "key"], "parent:::key" ; "with segments in between")]
    fn combine_with_empty_segment_leaves_delimiter(segments: &[&str], expected: &str) {
        // arrange

        // act
        let path = ConfigurationPath::combine(segments);

        // assert
        assert_eq!(&path, expected);
    }

    #[test_case("", "" ; "when empty")]
    #[test_case(":::", "" ; "when only delimiters")]
    #[test_case("a::b:::c", "c" ; "with empty segments in the middle")]
    #[test_case("a:::b:", "" ; "when last segment is empty")]
    #[test_case("key", "key" ; "with no parent")]
    #[test_case(":key", "key" ; "with 1 empty parent")]
    #[test_case("::key", "key" ; "with 2 empty parents")]
    #[test_case("parent:key", "key" ; "with parent")]
    fn section_key_should_return_expected_segment(path: &str, expected: &str) {
        // arrange

        // act
        let key = ConfigurationPath::section_key(path);

        // assert
        assert_eq!(key, expected);
    }

    #[test_case("", "" ; "when empty")]
    #[test_case(":::", "::" ; "when only delimiters")]
    #[test_case("a::b:::c", "a::b::" ; "with empty segments in the middle")]
    #[test_case("a:::b:", "a:::b" ; "when last segment is empty")]
    #[test_case("key", "" ; "with no parent")]
    #[test_case(":key", "" ; "with 1 empty parent")]
    #[test_case("::key", ":" ; "with 2 empty parents")]
    #[test_case("parent:key", "parent" ; "with parent")]
    fn parent_path_should_return_expected_segment(path: &str, expected: &str) {
        // arrange

        // act
        let key = ConfigurationPath::parent_path(path);

        // assert
        assert_eq!(key, expected);
    }
}