spacetimedb_query_builder/
expr.rs1use spacetimedb_lib::{
2 sats::{i256, u256},
3 ConnectionId, Identity, Timestamp,
4};
5
6use crate::{Col, ColumnRef};
7
8pub enum Operand<T> {
9 Column(ColumnRef<T>),
10 Literal(LiteralValue),
11}
12
13pub enum BoolExpr<T> {
14 Eq(Operand<T>, Operand<T>),
15 Ne(Operand<T>, Operand<T>),
16 Gt(Operand<T>, Operand<T>),
17 Lt(Operand<T>, Operand<T>),
18 Gte(Operand<T>, Operand<T>),
19 Lte(Operand<T>, Operand<T>),
20 And(Box<BoolExpr<T>>, Box<BoolExpr<T>>),
21 Or(Box<BoolExpr<T>>, Box<BoolExpr<T>>),
22 Not(Box<BoolExpr<T>>),
23}
24
25impl<T> BoolExpr<T> {
26 pub fn and(self, other: BoolExpr<T>) -> BoolExpr<T> {
27 BoolExpr::And(Box::new(self), Box::new(other))
28 }
29
30 pub fn or(self, other: BoolExpr<T>) -> BoolExpr<T> {
31 BoolExpr::Or(Box::new(self), Box::new(other))
32 }
33
34 #[allow(clippy::should_implement_trait)]
35 pub fn not(self) -> BoolExpr<T> {
36 BoolExpr::Not(Box::new(self))
37 }
38}
39
40pub trait RHS<T, V> {
45 fn to_expr(self) -> Operand<T>;
46}
47
48impl<T, V> RHS<T, V> for Col<T, V> {
49 fn to_expr(self) -> Operand<T> {
50 Operand::Column(self.col)
51 }
52}
53
54fn format_bool_expr<T>(v: &Operand<T>) -> String {
55 match v {
56 Operand::Column(col) => col.fmt(),
57 Operand::Literal(lit) => lit.0.clone(),
58 }
59}
60
61pub fn format_expr<T>(expr: &BoolExpr<T>) -> String {
62 match expr {
63 BoolExpr::Eq(l, r) => format!("({} = {})", format_bool_expr(l), format_bool_expr(r)),
64 BoolExpr::Ne(l, r) => format!("({} <> {})", format_bool_expr(l), format_bool_expr(r)),
65 BoolExpr::Gt(l, r) => format!("({} > {})", format_bool_expr(l), format_bool_expr(r)),
66 BoolExpr::Lt(l, r) => format!("({} < {})", format_bool_expr(l), format_bool_expr(r)),
67 BoolExpr::Gte(l, r) => format!("({} >= {})", format_bool_expr(l), format_bool_expr(r)),
68 BoolExpr::Lte(l, r) => format!("({} <= {})", format_bool_expr(l), format_bool_expr(r)),
69 BoolExpr::And(a, b) => format!("({} AND {})", format_expr(a), format_expr(b)),
70 BoolExpr::Or(a, b) => format!("({} OR {})", format_expr(a), format_expr(b)),
71 BoolExpr::Not(inner) => format!("(NOT {})", format_expr(inner)),
72 }
73}
74
75#[derive(Clone, Debug)]
76pub struct LiteralValue(String);
77
78impl LiteralValue {
79 pub fn new(s: String) -> Self {
80 Self(s)
81 }
82}
83
84macro_rules! impl_rhs {
85 ($ty:ty, $formatter:expr) => {
86 impl<T> RHS<T, $ty> for $ty {
87 fn to_expr(self) -> Operand<T> {
88 Operand::Literal(LiteralValue($formatter(self)))
89 }
90 }
91 };
92}
93
94impl_rhs!(String, |v: String| format!("'{}'", v.replace('\'', "''")));
95impl_rhs!(&str, |v: &str| format!("'{}'", v.replace('\'', "''")));
96
97impl_rhs!(i8, |v: i8| v.to_string());
98impl_rhs!(i16, |v: i16| v.to_string());
99impl_rhs!(i32, |v: i32| v.to_string());
100impl_rhs!(i64, |v: i64| v.to_string());
101impl_rhs!(i128, |v: i128| v.to_string());
102
103impl_rhs!(u8, |v: u8| v.to_string());
104impl_rhs!(u16, |v: u16| v.to_string());
105impl_rhs!(u32, |v: u32| v.to_string());
106impl_rhs!(u64, |v: u64| v.to_string());
107impl_rhs!(u128, |v: u128| v.to_string());
108impl_rhs!(usize, |v: usize| v.to_string());
109
110impl_rhs!(u256, |v: u256| v.to_string());
111impl_rhs!(i256, |v: i256| v.to_string());
112
113impl_rhs!(f32, |v: f32| (v as f64).to_string());
114impl_rhs!(f64, |v: f64| v.to_string());
115
116impl_rhs!(bool, |b: bool| if b { "TRUE".into() } else { "FALSE".into() });
117
118impl_rhs!(Identity, |id: Identity| format!("0x{}", id.to_hex()));
119impl_rhs!(ConnectionId, |id: ConnectionId| format!("0x{}", id.to_hex()));
120impl_rhs!(Timestamp, |ts: Timestamp| format!("'{}'", ts));
121
122impl_rhs!(Vec<u8>, |b: Vec<u8>| {
123 let hex: String = b.iter().map(|x| format!("{:02x}", x)).collect();
124 format!("0x{}", hex)
125});