1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/// The configuration to be used when extracting frontmatter.
#[derive(Debug)]
pub struct Config<'a> {
    number: Option<usize>,
    end_line: Option<&'a str>,
    lead_chars: Option<&'a str>,
    discard_first: bool,
}

impl<'a> Config<'a> {
    /// Defines a new set of extraction configuration settings.
    ///
    /// # Arguments
    ///
    /// | Argument | Description |
    /// | --- | --- |
    /// | `number` | The exact number of lines to extract, ignoring `end_line`; omit for auto-detection based on `end_line` or consecutive lines of `lead_chars` |
    /// | `end_line` | Treat the first occurrence of this string (alone on a line except for `lead_chars`) as the end of frontmatter |
    /// | `lead_chars` | Strip this string from the start of extracted frontmatter lines; consecutive lines starting with this string are treated as frontmatter lines if `number` and `end_line` are not specified |
    /// | `discard_first` | Whether or not to discard the entire first line that is extracted |
    ///
    /// # Panics
    ///
    /// This function will panic if none of `number`, `end_line`, and `lead_chars` have been specified.
    ///
    /// # Example
    ///
    /// ```rust
    /// use extract_frontmatter::Config;
    /// let config = Config::new(None, Some("-->"), None, true);
    /// ```
    pub fn new(
        number: Option<usize>,
        end_line: Option<&'a str>,
        lead_chars: Option<&'a str>,
        discard_first: bool,
    ) -> Config<'a> {
        let config = Config {
            end_line,
            number,
            lead_chars,
            discard_first,
        };

        assert!(config.validate(), "Invalid configuration settings.");
        config
    }

    pub const fn get_number(&self) -> Option<usize> {
        self.number
    }

    pub const fn get_end_line(&self) -> Option<&str> {
        self.end_line
    }

    pub const fn get_lead_chars(&self) -> Option<&str> {
        self.lead_chars
    }

    pub const fn discard_first(&self) -> bool {
        self.discard_first
    }

    fn validate(&self) -> bool {
        self.end_line.is_some() || self.number.is_some() || self.lead_chars.is_some()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const NUMBER: usize = 3;
    const END_LINE: &str = "-->";
    const LEAD_CHARS: &str = "// ";
    const DISCARD_FIRST: bool = true;

    const CONFIG: Config = Config {
        number: Some(NUMBER),
        end_line: Some(END_LINE),
        lead_chars: Some(LEAD_CHARS),
        discard_first: DISCARD_FIRST,
    };

    #[test]
    fn test_new_config() {
        let config = Config::new(Some(NUMBER), Some(END_LINE), Some(LEAD_CHARS), DISCARD_FIRST);
        assert_eq!(config, CONFIG)
    }

    #[test]
    #[should_panic]
    fn test_empty_config_panics() {
        Config::new(None, None, None, false);
    }

    #[test]
    fn test_get_number() {
        assert_eq!(CONFIG.get_number(), CONFIG.number)
    }

    #[test]
    fn test_get_end_line() {
        assert_eq!(CONFIG.get_end_line(), CONFIG.end_line)
    }

    #[test]
    fn test_get_lead_chars() {
        assert_eq!(CONFIG.get_lead_chars(), CONFIG.lead_chars)
    }

    #[test]
    fn test_discard_first() {
        assert_eq!(CONFIG.discard_first(), CONFIG.discard_first)
    }

    impl<'a> PartialEq for Config<'a> {
        fn eq(&self, other: &Self) -> bool {
            self.number == other.number
                && self.end_line == other.end_line
                && self.lead_chars == other.lead_chars
                && self.discard_first == other.discard_first
        }
    }
}