Skip to main content

cala_cel_interpreter/
value.rs

1use cel::objects::{Key, Map, Opaque, Value};
2use chrono::{DateTime, FixedOffset, NaiveDate, TimeZone, Utc};
3use rust_decimal::Decimal;
4use uuid::Uuid;
5
6use std::{collections::HashMap, sync::Arc};
7
8use crate::{cel_type::*, error::*};
9
10pub struct CelResult<'a> {
11    pub expr: &'a str,
12    pub val: CelValue,
13}
14
15#[derive(Debug, Clone, Eq, PartialEq)]
16pub(crate) struct CelDecimal(pub Decimal);
17
18impl Opaque for CelDecimal {
19    fn runtime_type_name(&self) -> &str {
20        "cala.Decimal"
21    }
22}
23
24#[derive(Debug, Clone, Eq, PartialEq)]
25pub(crate) struct CelUuid(pub Uuid);
26
27impl Opaque for CelUuid {
28    fn runtime_type_name(&self) -> &str {
29        "cala.Uuid"
30    }
31}
32
33#[derive(Debug, Clone, PartialEq)]
34pub enum CelValue {
35    // Builtins
36    Map(Arc<CelMap>),
37    List(Arc<CelArray>),
38    Int(i64),
39    UInt(u64),
40    Double(f64),
41    String(Arc<String>),
42    Bytes(Arc<Vec<u8>>),
43    Bool(bool),
44    Null,
45
46    // Abstract
47    Decimal(Decimal),
48    Date(NaiveDate),
49    Timestamp(DateTime<Utc>),
50    Uuid(Uuid),
51}
52
53#[derive(Debug, PartialEq)]
54pub struct CelMap {
55    inner: HashMap<CelKey, CelValue>,
56}
57
58impl CelMap {
59    pub fn new() -> Self {
60        Self {
61            inner: HashMap::new(),
62        }
63    }
64
65    pub fn insert(&mut self, k: impl Into<CelKey>, val: impl Into<CelValue>) {
66        self.inner.insert(k.into(), val.into());
67    }
68
69    pub fn get(&self, key: impl Into<CelKey>) -> CelValue {
70        self.inner
71            .get(&key.into())
72            .cloned()
73            .unwrap_or(CelValue::Null)
74    }
75
76    pub fn contains_key(&self, key: impl Into<CelKey>) -> bool {
77        self.inner.contains_key(&key.into())
78    }
79}
80
81impl Default for CelMap {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87impl From<HashMap<String, CelValue>> for CelMap {
88    fn from(map: HashMap<String, CelValue>) -> Self {
89        let mut res = CelMap::new();
90        for (k, v) in map {
91            res.insert(CelKey::String(Arc::from(k)), v);
92        }
93        res
94    }
95}
96
97impl From<CelMap> for CelValue {
98    fn from(m: CelMap) -> Self {
99        CelValue::Map(Arc::from(m))
100    }
101}
102
103impl CelValue {
104    pub(crate) fn into_cel_value(self) -> Value {
105        match self {
106            CelValue::Map(map) => {
107                let map = map
108                    .inner
109                    .iter()
110                    .map(|(k, v)| (k.clone().into_cel_key(), v.clone().into_cel_value()))
111                    .collect::<HashMap<_, _>>();
112                Value::Map(Map { map: Arc::new(map) })
113            }
114            CelValue::List(array) => Value::List(Arc::new(
115                array
116                    .inner
117                    .iter()
118                    .map(|v| v.clone().into_cel_value())
119                    .collect(),
120            )),
121            CelValue::Int(i) => Value::Int(i),
122            CelValue::UInt(u) => Value::UInt(u),
123            CelValue::Double(d) => Value::Float(d),
124            CelValue::String(s) => Value::String(s),
125            CelValue::Bytes(b) => Value::Bytes(b),
126            CelValue::Bool(b) => Value::Bool(b),
127            CelValue::Null => Value::Null,
128            CelValue::Decimal(d) => Value::Opaque(Arc::new(CelDecimal(d))),
129            CelValue::Date(d) => {
130                let dt = d.and_hms_opt(0, 0, 0).expect("midnight is valid");
131                Value::Timestamp(
132                    FixedOffset::east_opt(0)
133                        .expect("UTC offset is valid")
134                        .from_utc_datetime(&dt),
135                )
136            }
137            CelValue::Timestamp(ts) => Value::Timestamp(ts.fixed_offset()),
138            CelValue::Uuid(id) => Value::Opaque(Arc::new(CelUuid(id))),
139        }
140    }
141
142    pub(crate) fn from_cel_value(value: Value) -> Result<Self, CelError> {
143        Ok(match value {
144            Value::Map(map) => {
145                let mut res = CelMap::new();
146                for (k, v) in map.map.iter() {
147                    res.inner
148                        .insert(CelKey::try_from(k)?, CelValue::from_cel_value(v.clone())?);
149                }
150                CelValue::Map(Arc::new(res))
151            }
152            Value::List(values) => {
153                let mut res = CelArray::new();
154                for value in values.iter() {
155                    res.push(CelValue::from_cel_value(value.clone())?);
156                }
157                CelValue::List(Arc::new(res))
158            }
159            Value::Int(i) => CelValue::Int(i),
160            Value::UInt(u) => CelValue::UInt(u),
161            Value::Float(f) => CelValue::Double(f),
162            Value::String(s) => CelValue::String(s),
163            Value::Bytes(b) => CelValue::Bytes(b),
164            Value::Bool(b) => CelValue::Bool(b),
165            Value::Duration(d) => CelValue::String(Arc::new(format!("{d:?}"))),
166            Value::Timestamp(ts) => CelValue::Timestamp(ts.with_timezone(&Utc)),
167            Value::Opaque(o) if o.runtime_type_name() == "cala.Decimal" => {
168                let decimal = o.downcast_ref::<CelDecimal>().ok_or_else(|| {
169                    CelError::Unexpected("Could not downcast decimal".to_string())
170                })?;
171                CelValue::Decimal(decimal.0)
172            }
173            Value::Opaque(o) if o.runtime_type_name() == "cala.Uuid" => {
174                let id = o
175                    .downcast_ref::<CelUuid>()
176                    .ok_or_else(|| CelError::Unexpected("Could not downcast uuid".to_string()))?;
177                CelValue::Uuid(id.0)
178            }
179            Value::Opaque(o) => {
180                return Err(CelError::Unexpected(format!(
181                    "Unsupported opaque value {}",
182                    o.runtime_type_name()
183                )))
184            }
185            Value::Null => CelValue::Null,
186            Value::Function(_, _) => {
187                return Err(CelError::Unexpected(
188                    "Cannot convert function value".to_string(),
189                ))
190            }
191        })
192    }
193}
194
195#[derive(Debug, PartialEq)]
196pub struct CelArray {
197    inner: Vec<CelValue>,
198}
199
200impl CelArray {
201    pub fn new() -> Self {
202        Self { inner: Vec::new() }
203    }
204
205    pub fn push(&mut self, elem: impl Into<CelValue>) {
206        self.inner.push(elem.into());
207    }
208}
209
210impl Default for CelArray {
211    fn default() -> Self {
212        Self::new()
213    }
214}
215
216impl From<i64> for CelValue {
217    fn from(i: i64) -> Self {
218        CelValue::Int(i)
219    }
220}
221
222impl From<Decimal> for CelValue {
223    fn from(d: Decimal) -> Self {
224        CelValue::Decimal(d)
225    }
226}
227
228impl From<bool> for CelValue {
229    fn from(b: bool) -> Self {
230        CelValue::Bool(b)
231    }
232}
233
234impl From<String> for CelValue {
235    fn from(s: String) -> Self {
236        CelValue::String(Arc::from(s))
237    }
238}
239
240impl From<NaiveDate> for CelValue {
241    fn from(d: NaiveDate) -> Self {
242        CelValue::Date(d)
243    }
244}
245
246impl From<Uuid> for CelValue {
247    fn from(id: Uuid) -> Self {
248        CelValue::Uuid(id)
249    }
250}
251
252impl From<&str> for CelValue {
253    fn from(s: &str) -> Self {
254        CelValue::String(Arc::from(s.to_string()))
255    }
256}
257
258impl From<serde_json::Value> for CelValue {
259    fn from(v: serde_json::Value) -> Self {
260        use serde_json::Value::*;
261        match v {
262            Null => CelValue::Null,
263            Bool(b) => CelValue::Bool(b),
264            Number(n) => {
265                if let Some(u) = n.as_u64() {
266                    CelValue::UInt(u)
267                } else if let Some(i) = n.as_i64() {
268                    CelValue::Int(i)
269                } else if let Some(f) = n.as_f64() {
270                    CelValue::Double(f)
271                } else {
272                    unimplemented!()
273                }
274            }
275            String(s) => CelValue::String(Arc::from(s)),
276            Object(o) => {
277                let mut map = CelMap::new();
278                for (k, v) in o.into_iter() {
279                    map.insert(CelKey::String(Arc::from(k)), CelValue::from(v));
280                }
281                CelValue::Map(Arc::from(map))
282            }
283            Array(a) => {
284                let mut ar = CelArray::new();
285                for v in a.into_iter() {
286                    ar.push(CelValue::from(v));
287                }
288                CelValue::List(Arc::from(ar))
289            }
290        }
291    }
292}
293
294#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
295pub enum CelKey {
296    Int(i64),
297    UInt(u64),
298    Bool(bool),
299    String(Arc<String>),
300}
301
302impl CelKey {
303    fn into_cel_key(self) -> Key {
304        match self {
305            CelKey::Int(i) => Key::Int(i),
306            CelKey::UInt(u) => Key::Uint(u),
307            CelKey::Bool(b) => Key::Bool(b),
308            CelKey::String(s) => Key::String(s),
309        }
310    }
311}
312
313impl TryFrom<&Key> for CelKey {
314    type Error = CelError;
315
316    fn try_from(key: &Key) -> Result<Self, Self::Error> {
317        Ok(match key {
318            Key::Int(i) => CelKey::Int(*i),
319            Key::Uint(u) => CelKey::UInt(*u),
320            Key::Bool(b) => CelKey::Bool(*b),
321            Key::String(s) => CelKey::String(s.clone()),
322        })
323    }
324}
325
326impl From<&str> for CelKey {
327    fn from(s: &str) -> Self {
328        CelKey::String(Arc::from(s.to_string()))
329    }
330}
331
332impl From<String> for CelKey {
333    fn from(s: String) -> Self {
334        CelKey::String(Arc::from(s))
335    }
336}
337
338impl From<&Arc<String>> for CelKey {
339    fn from(s: &Arc<String>) -> Self {
340        CelKey::String(s.clone())
341    }
342}
343
344impl From<&CelValue> for CelType {
345    fn from(v: &CelValue) -> Self {
346        match v {
347            CelValue::Map(_) => CelType::Map,
348            CelValue::List(_) => CelType::List,
349            CelValue::Int(_) => CelType::Int,
350            CelValue::UInt(_) => CelType::UInt,
351            CelValue::Double(_) => CelType::Double,
352            CelValue::String(_) => CelType::String,
353            CelValue::Bytes(_) => CelType::Bytes,
354            CelValue::Bool(_) => CelType::Bool,
355            CelValue::Null => CelType::Null,
356
357            CelValue::Decimal(_) => CelType::Decimal,
358            CelValue::Date(_) => CelType::Date,
359            CelValue::Uuid(_) => CelType::Uuid,
360            CelValue::Timestamp(_) => CelType::Timestamp,
361        }
362    }
363}
364
365impl From<DateTime<Utc>> for CelValue {
366    fn from(d: DateTime<Utc>) -> Self {
367        CelValue::Timestamp(d)
368    }
369}
370
371impl TryFrom<&CelValue> for Arc<String> {
372    type Error = CelError;
373
374    fn try_from(v: &CelValue) -> Result<Self, Self::Error> {
375        if let CelValue::String(s) = v {
376            Ok(s.clone())
377        } else {
378            Err(CelError::BadType(CelType::String, CelType::from(v)))
379        }
380    }
381}
382
383impl<'a> TryFrom<&'a CelValue> for &'a Decimal {
384    type Error = CelError;
385
386    fn try_from(v: &'a CelValue) -> Result<Self, Self::Error> {
387        if let CelValue::Decimal(d) = v {
388            Ok(d)
389        } else {
390            Err(CelError::BadType(CelType::Decimal, CelType::from(v)))
391        }
392    }
393}
394
395impl TryFrom<CelResult<'_>> for bool {
396    type Error = ResultCoercionError;
397
398    fn try_from(CelResult { expr, val }: CelResult) -> Result<Self, Self::Error> {
399        if let CelValue::Bool(b) = val {
400            Ok(b)
401        } else {
402            Err(ResultCoercionError::BadCoreTypeCoercion(
403                format!("{expr:?}"),
404                CelType::from(&val),
405                CelType::Bool,
406            ))
407        }
408    }
409}
410
411impl TryFrom<CelResult<'_>> for NaiveDate {
412    type Error = ResultCoercionError;
413
414    fn try_from(CelResult { expr, val }: CelResult) -> Result<Self, Self::Error> {
415        match val {
416            CelValue::Date(d) => Ok(d),
417            CelValue::Timestamp(ts) => Ok(ts.date_naive()),
418            _ => Err(ResultCoercionError::BadCoreTypeCoercion(
419                expr.to_string(),
420                CelType::from(&val),
421                CelType::Date,
422            )),
423        }
424    }
425}
426
427impl TryFrom<CelResult<'_>> for DateTime<Utc> {
428    type Error = ResultCoercionError;
429
430    fn try_from(CelResult { expr, val }: CelResult) -> Result<Self, Self::Error> {
431        if let CelValue::Timestamp(d) = val {
432            Ok(d)
433        } else {
434            Err(ResultCoercionError::BadCoreTypeCoercion(
435                expr.to_string(),
436                CelType::from(&val),
437                CelType::Timestamp,
438            ))
439        }
440    }
441}
442
443impl TryFrom<CelResult<'_>> for Uuid {
444    type Error = ResultCoercionError;
445
446    fn try_from(CelResult { expr, val }: CelResult) -> Result<Self, Self::Error> {
447        if let CelValue::Uuid(id) = val {
448            Ok(id)
449        } else {
450            Err(ResultCoercionError::BadCoreTypeCoercion(
451                expr.to_string(),
452                CelType::from(&val),
453                CelType::Uuid,
454            ))
455        }
456    }
457}
458
459impl TryFrom<CelResult<'_>> for String {
460    type Error = ResultCoercionError;
461
462    fn try_from(CelResult { expr, val }: CelResult) -> Result<Self, Self::Error> {
463        if let CelValue::String(s) = val {
464            Ok(s.to_string())
465        } else {
466            Err(ResultCoercionError::BadCoreTypeCoercion(
467                expr.to_string(),
468                CelType::from(&val),
469                CelType::String,
470            ))
471        }
472    }
473}
474
475impl TryFrom<CelResult<'_>> for Decimal {
476    type Error = ResultCoercionError;
477
478    fn try_from(CelResult { expr, val }: CelResult) -> Result<Self, Self::Error> {
479        match val {
480            CelValue::Decimal(n) => Ok(n),
481            _ => Err(ResultCoercionError::BadCoreTypeCoercion(
482                expr.to_string(),
483                CelType::from(&val),
484                CelType::Decimal,
485            )),
486        }
487    }
488}
489
490impl From<&CelKey> for CelType {
491    fn from(v: &CelKey) -> Self {
492        match v {
493            CelKey::Int(_) => CelType::Int,
494            CelKey::UInt(_) => CelType::UInt,
495            CelKey::Bool(_) => CelType::Bool,
496            CelKey::String(_) => CelType::String,
497        }
498    }
499}
500
501impl TryFrom<&CelKey> for String {
502    type Error = ResultCoercionError;
503
504    fn try_from(v: &CelKey) -> Result<Self, Self::Error> {
505        if let CelKey::String(s) = v {
506            Ok(s.to_string())
507        } else {
508            Err(ResultCoercionError::BadCoreTypeCoercion(
509                format!("{v:?}"),
510                CelType::from(v),
511                CelType::String,
512            ))
513        }
514    }
515}
516
517impl TryFrom<CelResult<'_>> for serde_json::Value {
518    type Error = ResultCoercionError;
519
520    fn try_from(CelResult { expr, val }: CelResult) -> Result<Self, Self::Error> {
521        use serde_json::*;
522        Ok(match val {
523            CelValue::Int(n) => Value::from(n),
524            CelValue::UInt(n) => Value::from(n),
525            CelValue::Double(n) => Value::from(n.to_string()),
526            CelValue::Bool(b) => Value::from(b),
527            CelValue::String(n) => Value::from(n.as_str()),
528            CelValue::Null => Value::Null,
529            CelValue::Date(d) => Value::from(d.to_string()),
530            CelValue::Timestamp(d) => Value::from(d.to_rfc3339()),
531            CelValue::Uuid(u) => Value::from(u.to_string()),
532            CelValue::Map(m) => {
533                let mut res = serde_json::Map::new();
534                for (k, v) in m.inner.iter() {
535                    let key: String = k.try_into()?;
536                    let value = Self::try_from(CelResult {
537                        expr,
538                        val: v.clone(),
539                    })?;
540                    res.insert(key, value);
541                }
542                Value::from(res)
543            }
544            CelValue::List(a) => {
545                let mut res = Vec::new();
546                for v in a.inner.iter() {
547                    res.push(Self::try_from(CelResult {
548                        expr,
549                        val: v.clone(),
550                    })?);
551                }
552                Value::from(res)
553            }
554            CelValue::Decimal(d) => Value::from(d.to_string()),
555            CelValue::Bytes(_) => {
556                return Err(ResultCoercionError::BadExternalTypeCoercion(
557                    expr.to_string(),
558                    CelType::Bytes,
559                    "serde_json::Value",
560                ));
561            }
562        })
563    }
564}