1use crate::ops::{BinaryOp, RollupOp, ScheduleOp, SelectOp, TrinaryOp, UnaryOp};
2use crate::{ExprResult, ProjectExpr};
3use radiate_utils::{AnyValue, SmallStr};
4use radiate_utils::{WindowBuffer, sentry_id};
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7use std::{fmt::Debug, time::Duration};
8
9sentry_id!(ExprId);
10
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12#[derive(Clone, Debug, PartialEq)]
13pub enum ExprNode {
14 Literal(AnyValue<'static>),
15 Selector(SelectOp),
16 Schedule(ScheduleOp),
17 Rolling {
18 child: Box<Expr>,
19 buffer: WindowBuffer<AnyValue<'static>>,
20 },
21 Reduce {
22 child: Box<Expr>,
23 rollup: RollupOp,
24 },
25 Unary {
26 child: Box<Expr>,
27 op: UnaryOp,
28 },
29 Binary {
30 lhs: Box<Expr>,
31 rhs: Box<Expr>,
32 op: BinaryOp,
33 },
34 Trinary {
35 first: Box<Expr>,
36 second: Box<Expr>,
37 third: Box<Expr>,
38 op: TrinaryOp,
39 },
40}
41
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43#[derive(Clone, PartialEq)]
44pub struct Expr {
45 pub(crate) name: SmallStr,
46 pub(crate) id: ExprId,
47 pub(crate) node: ExprNode,
48}
49
50impl Expr {
51 pub fn new(node: ExprNode) -> Self {
52 let id = ExprId::new();
53 Self {
54 name: SmallStr::from_string(format!("Expr<{:?}>", id.get())),
55 id,
56 node,
57 }
58 }
59
60 pub fn id(&self) -> ExprId {
61 self.id
62 }
63
64 pub fn name(&self) -> &str {
65 self.name.as_str()
66 }
67
68 pub fn alias(mut self, name: impl Into<SmallStr>) -> Self {
69 self.name = name.into();
70 self
71 }
72
73 pub fn is_literal(&self) -> bool {
74 matches!(self.node, ExprNode::Literal(_))
75 }
76
77 pub fn is_selector(&self) -> bool {
78 matches!(self.node, ExprNode::Selector(_))
79 }
80
81 pub fn is_schedule(&self) -> bool {
82 matches!(self.node, ExprNode::Schedule(_))
83 }
84
85 pub fn into_schedule(self) -> Option<Expr> {
86 if self.is_schedule() {
87 return Some(self);
88 }
89
90 if let ExprNode::Literal(value) = &self.node {
91 if value.is_numeric() {
92 return value
93 .extract::<usize>()
94 .map(|interval| Expr::every(interval).into());
95 } else if value.is_duration() {
96 return value
97 .extract::<f32>()
98 .map(|seconds| Expr::throttle(Duration::from_secs_f32(seconds)).into());
99 }
100 }
101
102 Some(self)
103 }
104
105 pub fn walk(&self, f: &mut impl FnMut(&Expr)) {
106 f(self);
107 for child in self.children() {
108 child.walk(f);
109 }
110 }
111
112 fn children(&self) -> Vec<&Expr> {
113 match &self.node {
114 ExprNode::Literal(_) | ExprNode::Selector(_) | ExprNode::Schedule(_) => vec![],
115 ExprNode::Rolling { child: r, .. } => vec![&r],
116 ExprNode::Reduce { child: r, .. } => vec![&r],
117 ExprNode::Unary { child: u, .. } => vec![&u],
118 ExprNode::Binary { lhs, rhs, .. } => vec![&lhs, &rhs],
119 ExprNode::Trinary {
120 first,
121 second,
122 third,
123 ..
124 } => vec![&first, &second, &third],
125 }
126 }
127}
128
129impl Expr {
130 #[inline]
131 pub fn trigger(&mut self) -> ExprResult<'static> {
132 self.evaluate(&AnyValue::Null).map(|val| val.into_static())
133 }
134
135 #[inline]
136 pub fn evaluate<'a>(&'a mut self, input: &'a impl ProjectExpr<'a>) -> ExprResult<'a> {
137 match &mut self.node {
138 ExprNode::Literal(value) => Ok(value.clone()),
139 ExprNode::Selector(selector) => input.select(selector),
140 ExprNode::Schedule(op) => super::eval::try_schedule(op),
141
142 ExprNode::Rolling { child, buffer } => super::eval::rolling_eval(child, input, buffer),
143 ExprNode::Reduce { child, rollup } => super::eval::reduce_eval(child, input, rollup),
144
145 ExprNode::Unary { child, op } => super::eval::unary_eval(child, op, input),
146 ExprNode::Binary { lhs, rhs, op } => super::eval::binary_eval(lhs, rhs, op, input),
147 ExprNode::Trinary {
148 first,
149 second,
150 third,
151 op,
152 } => super::eval::trinary_eval(first, second, third, op, input),
153 }
154 }
155}
156
157impl<'a> From<AnyValue<'a>> for Expr {
158 fn from(value: AnyValue<'a>) -> Self {
159 Expr::new(ExprNode::Literal(value.into_static()))
160 }
161}
162
163impl From<SelectOp> for Expr {
164 fn from(selector: SelectOp) -> Self {
165 Expr::new(ExprNode::Selector(selector))
166 }
167}
168
169impl Debug for Expr {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 write!(f, "{:?}", self.node)
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn test_walk_literal() {
181 let expr = Expr::select("one")
182 .rolling(10)
183 .mean()
184 .div(10 as f32)
185 .clamp(1.0_f32, 5.0_f32);
186
187 fn print(expr: &Expr, depth: usize) {
188 println!("{}{} {:?}", " ".repeat(depth), expr.name(), expr);
189 for child in expr.children() {
190 print(child, depth + 1);
191 }
192 }
193
194 print(&expr, 0);
195 }
196}