ccs2/ast/
rule_tree.rs

1use std::fmt::Display;
2
3use crate::ast::{
4    JoinedBy, Key, Origin, PropDef, Property, Selector,
5    dnf::{expand, to_dnf},
6    flatten,
7    formula::Formula,
8};
9
10#[derive(Clone, Debug, PartialEq)]
11pub struct RuleTreeNode {
12    expand_limit: u32,
13    pub formula: Formula,
14    pub children: Vec<RuleTreeNode>,
15    pub props: Vec<Property>,
16    pub constraints: Vec<Key>,
17}
18impl Default for RuleTreeNode {
19    fn default() -> Self {
20        Self::new(Formula::default())
21    }
22}
23impl RuleTreeNode {
24    pub fn new(formula: Formula) -> Self {
25        Self::with_expand_limit(formula, 100)
26    }
27
28    pub fn with_expand_limit(formula: Formula, expand_limit: u32) -> Self {
29        Self {
30            expand_limit,
31            formula,
32            children: Default::default(),
33            props: Default::default(),
34            constraints: Default::default(),
35        }
36    }
37
38    pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a RuleTreeNode> + 'a> {
39        Box::new(std::iter::once(self).chain(self.children.iter().flat_map(|c| c.iter())))
40    }
41
42    pub fn traverse(&mut self, selector: Selector) -> &mut RuleTreeNode {
43        let dnf = to_dnf(flatten(selector), self.expand_limit as usize);
44        let formula = expand(
45            [self.formula.clone(), dnf].into_iter(),
46            self.expand_limit as usize,
47        );
48        self.children
49            .push(RuleTreeNode::with_expand_limit(formula, self.expand_limit));
50
51        self.children.iter_mut().last().unwrap()
52    }
53
54    pub fn add_property(
55        &mut self,
56        name: impl ToString,
57        value: impl ToString,
58        origin: Origin,
59        should_override: bool,
60    ) {
61        self.props.push(
62            PropDef {
63                name: name.to_string(),
64                value: value.to_string(),
65                origin,
66                should_override,
67            }
68            .into(),
69        )
70    }
71
72    pub fn add_constraint(&mut self, key: Key) {
73        self.constraints.push(key);
74    }
75
76    pub fn stats(&self) -> Stats {
77        let mut stats = Stats::default();
78        self.accumulate_stats(&mut stats);
79        stats
80    }
81
82    fn accumulate_stats(&self, stats: &mut Stats) {
83        stats.nodes += 1;
84        stats.props += self.props.len();
85        stats.constraints += self.constraints.len();
86        stats.edges += self.children.len();
87        for node in &self.children {
88            node.accumulate_stats(stats);
89        }
90    }
91
92    pub fn label(&self) -> String {
93        self.formula.to_string()
94    }
95
96    pub fn color(&self) -> String {
97        if !self.props.is_empty() || !self.constraints.is_empty() {
98            "lightblue"
99        } else {
100            "transparent"
101        }
102        .to_string()
103    }
104}
105impl Display for RuleTreeNode {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(
108            f,
109            "RuleTreeNode{{formula='{}', constraints=[{}], children=[{}]}}",
110            self.formula,
111            self.constraints.iter().joined_by(", "),
112            self.children.iter().joined_by(", "),
113        )
114    }
115}
116
117#[derive(Default, Debug)]
118pub struct Stats {
119    nodes: usize,
120    props: usize,
121    constraints: usize,
122    edges: usize,
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128    use crate::ast::{NullResolver, macros::*, parse};
129    use pretty_assertions::assert_eq;
130
131    fn formula(selector: Selector) -> Formula {
132        to_dnf(selector, 100)
133    }
134
135    macro_rules! prop {
136        ($name:literal, $value:literal, $line:literal) => {
137            Property::new(
138                $name,
139                $value,
140                Origin {
141                    filename: "".into(),
142                    line_number: $line as u32,
143                },
144                false,
145            )
146        };
147    }
148
149    #[test]
150    fn multilevel_tree() {
151        let ccs = r#"
152            a, f b e, c {
153                c d {
154                    x = y
155                }
156                e f {
157                    foobar = abc
158                }
159            }
160            a, c, b e f : baz = quux
161        "#;
162        let n = parse(ccs, NullResolver()).unwrap();
163        let mut tree = RuleTreeNode::default();
164        n.add_to(&mut tree);
165
166        let expected = RuleTreeNode {
167            children: vec![
168                RuleTreeNode {
169                    formula: formula(OR!("a", "c", AND!("b", "e", "f"))),
170                    children: vec![
171                        RuleTreeNode {
172                            formula: formula(AND!("c", "d")),
173                            props: vec![prop!("x", "y", 4)],
174                            ..Default::default()
175                        },
176                        RuleTreeNode {
177                            formula: formula(OR!(
178                                AND!("a", "e", "f"),
179                                AND!("b", "e", "f"),
180                                AND!("c", "e", "f")
181                            )),
182                            props: vec![prop!("foobar", "abc", 7)],
183                            ..Default::default()
184                        },
185                    ],
186                    ..Default::default()
187                },
188                RuleTreeNode {
189                    formula: formula(OR!("a", "c", AND!("b", "e", "f"))),
190                    props: vec![prop!("baz", "quux", 10)],
191                    ..Default::default()
192                },
193            ],
194            ..Default::default()
195        };
196
197        assert_eq!(tree, expected);
198    }
199}