Skip to main content

radiate_expr/
expr.rs

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}