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}