1use serde::{Deserialize, Serialize};
4
5use super::super::AuraOn;
6
7#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
9#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
10#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
11#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
12pub struct FieldRead {
13 pub domain: String,
14 pub name: String,
15 #[serde(skip_serializing_if = "Option::is_none")]
16 pub key: Option<String>,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub on: Option<AuraOn>,
19}
20
21#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
23#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
24#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
25#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
26#[serde(rename_all = "snake_case")]
27#[non_exhaustive]
28pub enum CompareOp {
29 Gt,
30 Gte,
31 Lt,
32 Lte,
33 Eq,
34 Ne,
35}
36
37#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
39#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
40#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
41#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
42#[serde(rename_all = "snake_case")]
43#[non_exhaustive]
44pub enum ArithOp {
45 Add,
46 Sub,
47 Mul,
48 Div,
49 Mod,
50}
51
52#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
54#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
55#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
56#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
57#[serde(rename_all = "snake_case")]
58#[non_exhaustive]
59pub enum UnaryMathOp {
60 Floor,
61 Ceil,
62 Abs,
63}
64
65#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
67#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
68#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
69#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
70#[serde(rename_all = "snake_case")]
71#[non_exhaustive]
72pub enum MinMaxOp {
73 Min,
74 Max,
75}
76
77impl CompareOp {
78 #[must_use]
80 pub fn symbol(self) -> &'static str {
81 match self {
82 CompareOp::Gt => ">",
83 CompareOp::Gte => ">=",
84 CompareOp::Lt => "<",
85 CompareOp::Lte => "<=",
86 CompareOp::Eq => "==",
87 CompareOp::Ne => "!=",
88 }
89 }
90}
91
92impl std::fmt::Display for CompareOp {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 f.write_str(self.symbol())
95 }
96}
97
98impl ArithOp {
99 #[must_use]
101 pub fn symbol(self) -> &'static str {
102 match self {
103 ArithOp::Add => "+",
104 ArithOp::Sub => "-",
105 ArithOp::Mul => "*",
106 ArithOp::Div => "/",
107 ArithOp::Mod => "%",
108 }
109 }
110}
111
112impl std::fmt::Display for ArithOp {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 f.write_str(self.symbol())
115 }
116}
117
118impl UnaryMathOp {
119 #[must_use]
121 pub fn symbol(self) -> &'static str {
122 match self {
123 UnaryMathOp::Floor => "floor",
124 UnaryMathOp::Ceil => "ceil",
125 UnaryMathOp::Abs => "abs",
126 }
127 }
128}
129
130impl std::fmt::Display for UnaryMathOp {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 f.write_str(self.symbol())
133 }
134}
135
136impl MinMaxOp {
137 #[must_use]
139 pub fn symbol(self) -> &'static str {
140 match self {
141 MinMaxOp::Min => "min",
142 MinMaxOp::Max => "max",
143 }
144 }
145}
146
147impl std::fmt::Display for MinMaxOp {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 f.write_str(self.symbol())
150 }
151}
152
153#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
155#[serde(tag = "type", rename_all = "snake_case")]
156#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
157#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
158#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
159#[non_exhaustive]
160pub enum Condition {
161 Read {
162 #[serde(flatten)]
163 field: FieldRead,
164 },
165 Bool {
166 value: bool,
167 },
168 Int {
169 value: i64,
170 },
171 Float {
172 value: f64,
173 },
174 Var {
175 name: String,
176 },
177 Compare {
178 op: CompareOp,
179 left: Box<Condition>,
180 right: Box<Condition>,
181 },
182 And {
183 operands: Vec<Condition>,
184 },
185 Or {
186 operands: Vec<Condition>,
187 },
188 Not {
189 operand: Box<Condition>,
190 },
191 Arith {
192 op: ArithOp,
193 left: Box<Condition>,
194 right: Box<Condition>,
195 },
196 UnaryMath {
197 op: UnaryMathOp,
198 operand: Box<Condition>,
199 },
200 MinMax {
201 op: MinMaxOp,
202 left: Box<Condition>,
203 right: Box<Condition>,
204 },
205 IfThenElse {
206 condition: Box<Condition>,
207 then: Box<Condition>,
208 otherwise: Box<Condition>,
209 },
210}
211
212#[derive(Debug)]
213#[non_exhaustive]
214enum ChildIter<'a> {
215 Empty,
216 One(std::iter::Once<&'a Condition>),
217 Pair(std::array::IntoIter<&'a Condition, 2>),
218 Triple(std::array::IntoIter<&'a Condition, 3>),
219 Many(std::slice::Iter<'a, Condition>),
220}
221
222impl<'a> Iterator for ChildIter<'a> {
223 type Item = &'a Condition;
224
225 fn next(&mut self) -> Option<Self::Item> {
226 match self {
227 ChildIter::Empty => None,
228 ChildIter::One(iter) => iter.next(),
229 ChildIter::Pair(iter) => iter.next(),
230 ChildIter::Triple(iter) => iter.next(),
231 ChildIter::Many(iter) => iter.next(),
232 }
233 }
234}
235
236impl Condition {
237 pub fn children(&self) -> impl Iterator<Item = &Condition> {
239 match self {
240 Condition::Compare { left, right, .. }
241 | Condition::Arith { left, right, .. }
242 | Condition::MinMax { left, right, .. } => {
243 ChildIter::Pair([left.as_ref(), right.as_ref()].into_iter())
244 }
245 Condition::And { operands } | Condition::Or { operands } => {
246 ChildIter::Many(operands.iter())
247 }
248 Condition::Not { operand } | Condition::UnaryMath { operand, .. } => {
249 ChildIter::One(std::iter::once(operand.as_ref()))
250 }
251 Condition::IfThenElse {
252 condition,
253 then,
254 otherwise,
255 } => ChildIter::Triple(
256 [condition.as_ref(), then.as_ref(), otherwise.as_ref()].into_iter(),
257 ),
258 Condition::Read { .. }
259 | Condition::Bool { .. }
260 | Condition::Int { .. }
261 | Condition::Float { .. }
262 | Condition::Var { .. } => ChildIter::Empty,
263 }
264 }
265
266 pub fn walk<'a>(&'a self, visitor: &mut impl FnMut(&'a Condition)) {
268 self.walk_with_depth(&mut |condition, _depth| visitor(condition));
269 }
270
271 pub fn walk_with_depth<'a>(&'a self, visitor: &mut impl FnMut(&'a Condition, usize)) {
273 let mut stack: Vec<(&Condition, usize)> = vec![(self, 1)];
274
275 while let Some((node, depth)) = stack.pop() {
276 visitor(node, depth);
277 stack.extend(node.children().map(|child| (child, depth + 1)));
278 }
279 }
280
281 #[must_use]
283 pub fn field_reads(&self) -> Vec<&FieldRead> {
284 let mut reads = Vec::new();
285
286 self.walk(&mut |node| {
287 if let Condition::Read { field } = node {
288 reads.push(field);
289 }
290 });
291
292 reads
293 }
294}