Skip to main content

ownrs/
lib.rs

1pub use directive::Directive;
2pub use owner::Owner;
3pub use per_file::PerFile;
4pub use statement::Statement;
5
6use error::ParseError;
7
8pub mod error;
9
10mod directive;
11mod owner;
12mod per_file;
13mod statement;
14
15pub fn parse<T: AsRef<str>>(input: T) -> Result<Vec<Statement>, ParseError> {
16    let filtered = input
17        .as_ref()
18        .split("\n")
19        // Attach line numbers to each line.
20        .enumerate()
21        // Remove any inline comments from each line.
22        .map(|(line_num, content)| (line_num + 1, remove_inline_comments(content)))
23        // Ignore empty lines and comments.
24        .filter(|(_, content)| !content.is_empty() && !content.starts_with("#"));
25
26    let mut parsed = vec![];
27    for (line_num, content) in filtered {
28        statement::statement(content)
29            .map(|(_, output)| parsed.push(output))
30            .map_err(|e| ParseError::from_nom(line_num, e))?;
31    }
32    Ok(parsed)
33}
34
35fn is_whitespace(c: char) -> bool {
36    c == ' ' || c == '\t' || c == '\n'
37}
38
39// TODO: I imagine it is more efficient to use nom to ignore inline comments
40// rather than splitting the string. We should benchmark this and update the
41// parser if that is the case.
42fn remove_inline_comments(line: &str) -> &str {
43    line.trim().split("#").nth(0).unwrap()
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn empty() {
52        assert!(parse("").unwrap().is_empty());
53    }
54
55    #[test]
56    fn empty_ws() {
57        assert!(parse(" ").unwrap().is_empty());
58    }
59
60    #[test]
61    fn comment() {
62        assert!(parse("# comment").unwrap().is_empty());
63    }
64
65    #[test]
66    fn comment_ws() {
67        assert!(parse("#comment").unwrap().is_empty());
68    }
69
70    #[test]
71    fn inline_comment() {
72        assert_eq!(
73            parse("set noparent # comment").unwrap(),
74            vec![Statement::Directive(Directive::NoParent)]
75        );
76    }
77
78    #[test]
79    fn inline_comment_ws() {
80        assert_eq!(
81            parse("set noparent# comment").unwrap(),
82            vec![Statement::Directive(Directive::NoParent)]
83        );
84    }
85
86    #[test]
87    fn multiline() {
88        assert_eq!(
89            parse("set noparent\n*").unwrap(),
90            vec![
91                Statement::Directive(Directive::NoParent),
92                Statement::Directive(Directive::StarGlob)
93            ]
94        );
95    }
96
97    #[test]
98    fn multiline_trailing_newline() {
99        assert_eq!(
100            parse("set noparent\n*\n").unwrap(),
101            vec![
102                Statement::Directive(Directive::NoParent),
103                Statement::Directive(Directive::StarGlob)
104            ]
105        );
106    }
107
108    #[test]
109    fn multiline_leading_newline() {
110        assert_eq!(
111            parse("\nset noparent\n*").unwrap(),
112            vec![
113                Statement::Directive(Directive::NoParent),
114                Statement::Directive(Directive::StarGlob)
115            ]
116        );
117    }
118
119    #[test]
120    fn multiline_comments() {
121        assert_eq!(
122            parse("set noparent # comment\n* # comment").unwrap(),
123            vec![
124                Statement::Directive(Directive::NoParent),
125                Statement::Directive(Directive::StarGlob)
126            ]
127        );
128    }
129}