fapolicy_rules/
read.rs

1/*
2 * Copyright Concurrent Technologies Corporation 2021
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 */
8
9use std::path::PathBuf;
10
11use nom::branch::alt;
12use nom::character::complete::multispace0;
13use nom::combinator::{eof, map, recognize};
14use nom::error::{ErrorKind, ParseError};
15use nom::sequence::tuple;
16
17use crate::db::{Entry, DB};
18use crate::error::Error;
19use crate::linter::lint::lint_db;
20use crate::load::RuleFrom::{Disk, Mem};
21use crate::load::RuleSource;
22use crate::parser::parse::{StrTrace, TraceResult};
23use crate::parser::{comment, rule, set};
24use crate::read::Line::*;
25use crate::{load, Rule, Set};
26
27#[derive(Debug)]
28enum Line {
29    Blank,
30    Comment(String),
31    SetDef(Set),
32    RuleDef(Rule),
33    Malformed(String, String),
34    MalformedSet(String, String),
35}
36
37enum LineError<I> {
38    CannotParseSet(I, String),
39    CannotParse(I, String),
40    Nom(I, ErrorKind),
41}
42
43impl<I> ParseError<I> for LineError<I> {
44    fn from_error_kind(input: I, kind: ErrorKind) -> Self {
45        LineError::Nom(input, kind)
46    }
47
48    fn append(_: I, _: ErrorKind, other: Self) -> Self {
49        other
50    }
51}
52
53fn parser(i: &str) -> nom::IResult<StrTrace, Line, LineError<&str>> {
54    alt((
55        map(blank_line, |_| Blank),
56        map(comment::parse, Comment),
57        map(set::parse, SetDef),
58        map(rule::parse, RuleDef),
59    ))(StrTrace::new(i))
60    .map_err(|e| {
61        let details = match e {
62            nom::Err::Error(e) => e.to_string(),
63            e => format!("{:?}", e),
64        };
65        // todo;; guess set or rule here based on the line start char?
66        let f = if i.starts_with('%') {
67            LineError::CannotParseSet
68        } else {
69            LineError::CannotParse
70        };
71
72        nom::Err::Error(f(i, details))
73    })
74}
75
76fn blank_line(i: StrTrace) -> TraceResult<StrTrace> {
77    recognize(tuple((multispace0, eof)))(i)
78}
79
80pub fn deserialize_rules_db(text: &str) -> Result<DB, Error> {
81    read_rules_db(load::rules_from(Mem(text.to_string()))?)
82}
83
84pub fn load_rules_db(path: &str) -> Result<DB, Error> {
85    read_rules_db(load::rules_from(Disk(PathBuf::from(path)))?)
86}
87
88fn read_rules_db(xs: Vec<RuleSource>) -> Result<DB, Error> {
89    let lookup: Vec<(String, Entry)> = xs
90        .iter()
91        .map(relativized_path)
92        .map(|(source, l)| (source, parser(l)))
93        .flat_map(|(source, r)| match r {
94            Ok((t, rule)) if t.current.is_empty() => Some((source, rule)),
95            Ok((_, _)) => None,
96            Err(nom::Err::Error(LineError::CannotParse(i, why))) => {
97                Some((source, Malformed(i.to_string(), why)))
98            }
99            Err(nom::Err::Error(LineError::CannotParseSet(i, why))) => {
100                Some((source, MalformedSet(i.to_string(), why)))
101            }
102            Err(_) => None,
103        })
104        .filter_map(|(source, line)| match line {
105            RuleDef(r) => Some((source, Entry::ValidRule(r))),
106            SetDef(s) => Some((source, Entry::ValidSet(s))),
107            Malformed(text, error) => Some((source, Entry::Invalid { text, error })),
108            MalformedSet(text, error) => Some((source, Entry::InvalidSet { text, error })),
109            Comment(text) => Some((source, Entry::Comment(text))),
110            _ => None,
111        })
112        .collect();
113
114    Ok(lint_db(DB::from_sources(lookup)))
115}
116
117fn relativized_path(i: &(PathBuf, String)) -> (String, &String) {
118    (
119        // render and split off the filename from full path
120        i.0.display()
121            .to_string()
122            .rsplit_once('/')
123            .map(|(_, rhs)| rhs.to_string())
124            // if there was no / separator then use the full path
125            .unwrap_or_else(|| i.0.display().to_string()),
126        &i.1,
127    )
128}
129
130#[cfg(test)]
131mod tests {
132    use crate::read::relativized_path;
133    use std::path::PathBuf;
134
135    #[test]
136    fn test_relativize_path() {
137        // absolute path
138        let i = (PathBuf::from("/foo/bar.baz"), "boom".to_string());
139        let r = relativized_path(&i);
140        assert_eq!(r.0, "bar.baz");
141        assert_eq!(r.1, "boom");
142
143        // relative path
144        let i = (PathBuf::from("bar.baz"), "boom2".to_string());
145        let r = relativized_path(&i);
146        assert_eq!(r.0, "bar.baz");
147        assert_eq!(r.1, "boom2");
148    }
149}