cel_eval/
lib.rs

1#[cfg(not(target_arch = "wasm32"))]
2uniffi::include_scaffolding!("cel");
3pub mod ast;
4pub mod models;
5
6use crate::ast::{ASTExecutionContext, JSONExpression};
7use crate::models::PassableValue::Function;
8use crate::models::PassableValue::PMap;
9use crate::models::{ExecutionContext, PassableMap, PassableValue};
10use crate::ExecutableType::{CompiledProgram, AST};
11use async_trait::async_trait;
12use cel_interpreter::extractors::This;
13use cel_interpreter::objects::{Key, Map, TryIntoValue};
14use cel_interpreter::{Context, ExecutionError, Expression, FunctionContext, Program, Value};
15use cel_parser::parse;
16use std::collections::HashMap;
17use std::error::Error;
18use std::fmt;
19use std::fmt::Debug;
20use std::ops::Deref;
21use std::sync::{mpsc, Arc, Mutex};
22use std::thread::spawn;
23
24#[cfg(not(target_arch = "wasm32"))]
25use futures_lite::future::block_on;
26use uniffi::deps::log::__private_api::log;
27#[cfg(target_arch = "wasm32")]
28use wasm_bindgen_futures::spawn_local;
29
30/**
31 * Host context trait that defines the methods that the host context should implement,
32 * i.e. iOS or Android calling code. This trait is used to resolve dynamic properties in the
33 * CEL expression during evaluation, such as `computed.daysSinceEvent("event_name")` or similar.
34 * Note: Since WASM async support in the browser is still not fully mature, we're using the
35 * target_arch cfg to define the trait methods differently for WASM and non-WASM targets.
36 */
37#[cfg(target_arch = "wasm32")]
38pub trait HostContext: Send + Sync {
39    fn computed_property(&self, name: String, args: String) -> String;
40
41    fn device_property(&self, name: String, args: String) -> String;
42}
43
44#[cfg(not(target_arch = "wasm32"))]
45#[async_trait]
46pub trait HostContext: Send + Sync {
47    async fn computed_property(&self, name: String, args: String) -> String;
48
49    async fn device_property(&self, name: String, args: String) -> String;
50}
51
52/**
53 * Evaluate a CEL expression with the given AST
54 * @param ast The AST Execution Context, serialized as JSON. This defines the AST, the variables, and the platform properties.
55 * @param host The host context to use for resolving properties
56 * @return The result of the evaluation, either "true" or "false"
57 */
58pub fn evaluate_ast_with_context(definition: String, host: Arc<dyn HostContext>) -> String {
59    let data: Result<ASTExecutionContext, _> = serde_json::from_str(definition.as_str());
60    let data = match data {
61        Ok(data) => data,
62        Err(_) => {
63            let e: Result<_, String> =
64                Err::<ASTExecutionContext, String>("Invalid execution context JSON".to_string());
65            return serde_json::to_string(&e).unwrap();
66        }
67    };
68    let host = host.clone();
69    let res = execute_with(
70        AST(data.expression.into()),
71        data.variables,
72        data.computed,
73        data.device,
74        host,
75    )
76    .map(|val| val.to_passable())
77    .map_err(|err| err.to_string());
78    serde_json::to_string(&res).unwrap()
79}
80
81/**
82 * Evaluate a CEL expression with the given AST without any context
83 * @param ast The AST of the expression, serialized as JSON. This AST should contain already resolved dynamic variables.
84 * @return The result of the evaluation, either "true" or "false"
85 */
86pub fn evaluate_ast(ast: String) -> String {
87    let data: Result<JSONExpression, _> = serde_json::from_str(ast.as_str());
88    let data: JSONExpression = match data {
89        Ok(data) => data,
90        Err(_) => {
91            let e: Result<_, String> =
92                Err::<JSONExpression, String>("Invalid definition for AST Execution".to_string());
93            return serde_json::to_string(&e).unwrap();
94        }
95    };
96    let ctx = Context::default();
97    let res = ctx
98        .resolve(&data.into())
99        .map(|val| DisplayableValue(val.clone()).to_passable())
100        .map_err(|err| DisplayableError(err).to_string());
101    serde_json::to_string(&res).unwrap()
102}
103
104/**
105 * Evaluate a CEL expression with the given definition by compiling it first.
106 * @param definition The definition of the expression, serialized as JSON. This defines the expression, the variables, and the platform properties.
107 * @param host The host context to use for resolving properties
108 * @return The result of the evaluation, either "true" or "false"
109 */
110
111pub fn evaluate_with_context(definition: String, host: Arc<dyn HostContext>) -> String {
112    let data: Result<ExecutionContext, _> = serde_json::from_str(definition.as_str());
113    let data: ExecutionContext = match data {
114        Ok(data) => data,
115        Err(_) => {
116            let e: Result<ExecutionContext, String> =
117                Err("Invalid execution context JSON".to_string());
118            return serde_json::to_string(&e).unwrap();
119        }
120    };
121    let compiled =
122        Program::compile(data.expression.as_str()).map(|program| CompiledProgram(program));
123    let result = match compiled {
124        Ok(compiled) => execute_with(compiled, data.variables, data.computed, data.device, host)
125            .map(|val| val.to_passable())
126            .map_err(|err| err.to_string()),
127        Err(e) => Err("Failed to compile expression".to_string()),
128    };
129    serde_json::to_string(&result).unwrap()
130}
131
132/**
133 * Transforms a given CEL expression into a CEL AST, serialized as JSON.
134 * @param expression The CEL expression to parse
135 * @return The AST of the expression, serialized as JSON
136 */
137pub fn parse_to_ast(expression: String) -> String {
138    let ast: Result<JSONExpression, _> = parse(expression.as_str()).map(|expr| expr.into());
139    let ast = ast.map_err(|err| err.to_string());
140    serde_json::to_string(&ast.unwrap()).unwrap()
141}
142
143/**
144Type of expression to be executed, either a compiled program or an AST.
145 */
146enum ExecutableType {
147    AST(Expression),
148    CompiledProgram(Program),
149}
150
151/**
152 * Execute a CEL expression, either compiled or pure AST; with the given context.
153 * @param executable The executable type, either an AST or a compiled program
154 * @param variables The variables to use in the expression
155 * @param platform The platform properties or functions to use in the expression
156 * @param host The host context to use for resolving properties
157 */
158fn execute_with(
159    executable: ExecutableType,
160    variables: PassableMap,
161    computed: Option<HashMap<String, Vec<PassableValue>>>,
162    device: Option<HashMap<String, Vec<PassableValue>>>,
163    host: Arc<dyn HostContext + 'static>,
164) -> Result<DisplayableValue, DisplayableError> {
165    let host = host.clone();
166    let host = Arc::new(Mutex::new(host));
167    let mut ctx = Context::default();
168    // Isolate device to re-bind later
169    let device_map = variables.clone();
170    let device_map = device_map
171        .map
172        .get("device")
173        .clone()
174        .unwrap_or(&PMap(HashMap::new()))
175        .clone();
176
177    // Add predefined variables locally to the context
178    variables.map.iter().for_each(|it| {
179        let _ = ctx.add_variable(it.0.as_str(), it.1.to_cel());
180    });
181    // Add maybe function
182    ctx.add_function("maybe", maybe);
183
184    // This function is used to extract the value of a property from the host context
185    // As UniFFi doesn't support recursive enums yet, we have to pass it in as a
186    // JSON serialized string of a PassableValue from Host and deserialize it here
187
188    enum PropType {
189        Computed,
190        Device,
191    }
192    #[cfg(not(target_arch = "wasm32"))]
193    fn prop_for(
194        prop_type: PropType,
195        name: Arc<String>,
196        args: Option<Vec<PassableValue>>,
197        ctx: &Arc<dyn HostContext>,
198    ) -> Result<PassableValue, String> {
199        // Get computed property
200        let val = futures_lite::future::block_on(async move {
201            let ctx = ctx.clone();
202            let args = if let Some(args) = args {
203                serde_json::to_string(&args)
204            } else {
205                serde_json::to_string::<Vec<PassableValue>>(&vec![])
206            };
207            match args {
208                Ok(args) => match prop_type {
209                    PropType::Computed => {
210                        Ok(ctx.computed_property(name.clone().to_string(), args).await)
211                    }
212                    PropType::Device => {
213                        Ok(ctx.device_property(name.clone().to_string(), args).await)
214                    }
215                },
216                Err(e) => Err(ExecutionError::UndeclaredReference(name).to_string()),
217            }
218        });
219        // Deserialize the value
220        let passable: Result<PassableValue, String> = val
221            .map(|val| serde_json::from_str(val.as_str()).unwrap_or(PassableValue::Null))
222            .map_err(|err| err.to_string());
223
224        passable
225    }
226
227    #[cfg(target_arch = "wasm32")]
228    fn prop_for(
229        prop_type: PropType,
230        name: Arc<String>,
231        args: Option<Vec<PassableValue>>,
232        ctx: &Arc<dyn HostContext>,
233    ) -> Option<PassableValue> {
234        let ctx = ctx.clone();
235
236        let val = match prop_type {
237            PropType::Computed => ctx.computed_property(
238                name.clone().to_string(),
239                serde_json::to_string(&args)
240                    .expect("Failed to serialize args for computed property"),
241            ),
242            PropType::Device => ctx.device_property(
243                name.clone().to_string(),
244                serde_json::to_string(&args)
245                    .expect("Failed to serialize args for computed property"),
246            ),
247        };
248        // Deserialize the value
249        let passable: Option<PassableValue> =
250            serde_json::from_str(val.as_str()).unwrap_or(Some(PassableValue::Null));
251
252        passable
253    }
254
255    let computed = computed.unwrap_or(HashMap::new()).clone();
256
257    // Create computed properties as a map of keys and function names
258    let computed_host_properties: HashMap<Key, Value> = computed
259        .iter()
260        .map(|it| {
261            let args = it.1.clone();
262            let args = if args.is_empty() {
263                None
264            } else {
265                Some(Box::new(PassableValue::List(args)))
266            };
267            let name = it.0.clone();
268            (
269                Key::String(Arc::new(name.clone())),
270                Function(name, args).to_cel(),
271            )
272        })
273        .collect();
274
275    let device = device.unwrap_or(HashMap::new()).clone();
276
277    // From defined properties the device properties
278    let total_device_properties = if let PMap(map) = device_map {
279        map
280    } else {
281        HashMap::new()
282    };
283
284    // Create device properties as a map of keys and function names
285    let device_host_properties: HashMap<Key, Value> = device
286        .iter()
287        .map(|it| {
288            let args = it.1.clone();
289            let args = if args.is_empty() {
290                None
291            } else {
292                Some(Box::new(PassableValue::List(args)))
293            };
294            let name = it.0.clone();
295            (
296                Key::String(Arc::new(name.clone())),
297                Function(name, args).to_cel(),
298            )
299        })
300        .chain(
301            total_device_properties
302                .iter()
303                .map(|(k, v)| (Key::String(Arc::new(k.clone())), v.to_cel().clone())),
304        )
305        .collect();
306
307    // Add the map to the `computed` object
308    let _ = ctx.add_variable(
309        "computed",
310        Value::Map(Map {
311            map: Arc::new(computed_host_properties),
312        }),
313    );
314
315    // Add the map to the `device` object
316    let _ = ctx.add_variable(
317        "device",
318        Value::Map(Map {
319            map: Arc::new(device_host_properties),
320        }),
321    );
322
323    let binding = device.clone();
324    // Combine the device and computed properties
325    let host_properties = binding
326        .iter()
327        .chain(computed.iter())
328        .map(|(k, v)| (k.clone(), v.clone()))
329        .into_iter();
330
331    let mut device_properties_clone = device.clone().clone();
332    // Add those functions to the context
333    for it in host_properties {
334        let mut value = device_properties_clone.clone();
335        let key = it.0.clone();
336        let host_clone = Arc::clone(&host); // Clone the Arc to pass into the closure
337        let key_str = key.clone(); // Clone key for usage in the closure
338        ctx.add_function(
339            key_str.as_str(),
340            move |ftx: &FunctionContext| -> Result<Value, ExecutionError> {
341                let device = value.clone();
342                let fx = ftx.clone();
343                let name = fx.name.clone(); // Move the name into the closure
344                let args = fx.args.clone(); // Clone the arguments
345                let host = host_clone.lock(); // Lock the host for safe access
346                match host {
347                    Ok(host) => prop_for(
348                        if device.contains_key(&it.0) {
349                            PropType::Device
350                        } else {
351                            PropType::Computed
352                        },
353                        name.clone(),
354                        Some(
355                            args.iter()
356                                .map(|expression| {
357                                    DisplayableValue(ftx.ptx.resolve(expression).unwrap())
358                                        .to_passable()
359                                })
360                                .collect(),
361                        ),
362                        &*host,
363                    )
364                    .map_or(Err(ExecutionError::UndeclaredReference(name)), |v| {
365                        Ok(v.to_cel())
366                    }),
367                    Err(e) => {
368                        let e = e.to_string();
369                        let name = name.clone().to_string();
370                        let error = ExecutionError::FunctionError {
371                            function: name,
372                            message: e,
373                        };
374                        Err(error)
375                    }
376                }
377            },
378        );
379    }
380
381    let val = match executable {
382        AST(ast) => &ctx.resolve(&ast),
383        CompiledProgram(program) => &program.execute(&ctx),
384    };
385
386    val.clone()
387        .map(|val| DisplayableValue(val.clone()))
388        .map_err(|err| DisplayableError(err))
389}
390
391pub fn maybe(
392    ftx: &FunctionContext,
393    This(_this): This<Value>,
394    left: Expression,
395    right: Expression,
396) -> Result<Value, ExecutionError> {
397    return ftx.ptx.resolve(&left).or_else(|_| ftx.ptx.resolve(&right));
398}
399
400// Wrappers around CEL values used so that we can create extensions on them
401pub struct DisplayableValue(Value);
402
403pub struct DisplayableError(ExecutionError);
404
405impl fmt::Display for DisplayableValue {
406    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
407        match &self.0 {
408            Value::Int(i) => write!(f, "{}", i),
409            Value::Float(x) => write!(f, "{}", x),
410            Value::String(s) => write!(f, "{}", s),
411            // Add more variants as needed
412            Value::UInt(i) => write!(f, "{}", i),
413            Value::Bytes(_) => {
414                write!(f, "{}", "bytes go here")
415            }
416            Value::Bool(b) => write!(f, "{}", b),
417            Value::Duration(d) => write!(f, "{}", d),
418            Value::Timestamp(t) => write!(f, "{}", t),
419            Value::Null => write!(f, "{}", "null"),
420            Value::Function(name, _) => write!(f, "{}", name),
421            Value::Map(map) => {
422                let res: HashMap<String, String> = map
423                    .map
424                    .iter()
425                    .map(|(k, v)| {
426                        let key = DisplayableValue(k.try_into_value().unwrap().clone()).to_string();
427                        let value = DisplayableValue(v.clone()).to_string().replace("\\", "");
428                        (key, value)
429                    })
430                    .collect();
431                let map = serde_json::to_string(&res).unwrap();
432                write!(f, "{}", map)
433            }
434            Value::List(list) => write!(
435                f,
436                "{}",
437                list.iter()
438                    .map(|v| {
439                        let key = DisplayableValue(v.clone());
440                        return key.to_string();
441                    })
442                    .collect::<Vec<_>>()
443                    .join(",\n ")
444            ),
445        }
446    }
447}
448
449impl fmt::Display for DisplayableError {
450    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
451        write!(f, "{}", self.0.to_string().as_str())
452    }
453}
454
455#[cfg(test)]
456mod tests {
457    use super::*;
458
459    struct TestContext {
460        map: HashMap<String, String>,
461    }
462
463    #[async_trait]
464    impl HostContext for TestContext {
465        async fn computed_property(&self, name: String, args: String) -> String {
466            self.map.get(&name).unwrap().to_string()
467        }
468
469        async fn device_property(&self, name: String, args: String) -> String {
470            self.map.get(&name).unwrap().to_string()
471        }
472    }
473
474    #[tokio::test]
475    async fn test_variables() {
476        let ctx = Arc::new(TestContext {
477            map: HashMap::new(),
478        });
479        let res = evaluate_with_context(
480            r#"
481        {
482            "variables": {
483             "map" : {
484                    "foo": {"type": "int", "value": 100}
485            }},
486            "expression": "foo == 100"
487        }
488
489        "#
490            .to_string(),
491            ctx,
492        );
493        assert_eq!(res, "{\"Ok\":{\"type\":\"bool\",\"value\":true}}");
494    }
495
496    #[tokio::test]
497    async fn test_execution_with_ctx() {
498        let ctx = Arc::new(TestContext {
499            map: HashMap::new(),
500        });
501        let res = evaluate_with_context(
502            r#"
503        {
504            "variables": {
505             "map" : {
506                    "foo": {"type": "int", "value": 100},
507                    "bar": {"type": "int", "value": 42}
508            }},
509            "expression": "foo + bar == 142"
510        }
511
512        "#
513            .to_string(),
514            ctx,
515        );
516        assert_eq!(res, "{\"Ok\":{\"type\":\"bool\",\"value\":true}}");
517    }
518
519    #[test]
520    fn test_unknown_function_with_arg_fails_with_undeclared_ref() {
521        let ctx = Arc::new(TestContext {
522            map: HashMap::new(),
523        });
524
525        let res = evaluate_with_context(
526            r#"
527        {
528            "variables": {
529             "map" : {
530                    "foo": {"type": "int", "value": 100}
531            }},
532            "expression": "test_custom_func(foo) == 101"
533        }
534
535        "#
536            .to_string(),
537            ctx,
538        );
539        assert_eq!(
540            res,
541            "{\"Err\":\"Undeclared reference to 'test_custom_func'\"}"
542        );
543    }
544
545    #[test]
546    fn test_list_contains() {
547        let ctx = Arc::new(TestContext {
548            map: HashMap::new(),
549        });
550        let res = evaluate_with_context(
551            r#"
552        {
553            "variables": {
554                 "map" : {
555                    "numbers": {
556                        "type" : "list",
557                        "value" : [
558                            {"type": "int", "value": 1},
559                            {"type": "int", "value": 2},
560                            {"type": "int", "value": 3}
561                             ]
562                       }
563                 }
564            },
565            "expression": "numbers.contains(2)"
566        }
567
568        "#
569            .to_string(),
570            ctx,
571        );
572        assert_eq!(res, "{\"Ok\":{\"type\":\"bool\",\"value\":true}}");
573    }
574
575    #[tokio::test]
576    async fn test_execution_with_map() {
577        let ctx = Arc::new(TestContext {
578            map: HashMap::new(),
579        });
580        let res = evaluate_with_context(
581            r#"
582        {
583                    "variables": {
584                        "map": {
585                            "user": {
586                                "type": "map",
587                                "value": {
588                                    "should_display": {
589                                        "type": "bool",
590                                        "value": true
591                                    },
592                                    "some_value": {
593                                        "type": "uint",
594                                        "value": 13
595                                    }
596                                }
597                            }
598                        }
599                    },
600                    "expression": "user.should_display == true && user.some_value > 12"
601       }
602
603        "#
604            .to_string(),
605            ctx,
606        );
607        println!("{}", res.clone());
608        assert_eq!(res, "{\"Ok\":{\"type\":\"bool\",\"value\":true}}");
609    }
610
611    #[tokio::test]
612    async fn test_execution_with_failure() {
613        let ctx = Arc::new(TestContext {
614            map: HashMap::new(),
615        });
616        let res = evaluate_with_context(
617            r#"
618        {
619                    "variables": {
620                        "map": {
621                            "user": {
622                                "type": "map",
623                                "value": {
624                                    "some_value": {
625                                        "type": "uint",
626                                        "value": 13
627                                    }
628                                }
629                            }
630                        }
631                    },
632                    "expression": "user.should_display == true && user.some_value > 12"
633       }
634
635        "#
636            .to_string(),
637            ctx,
638        );
639        println!("{}", res.clone());
640        assert_eq!(res, "{\"Err\":\"No such key: should_display\"}");
641    }
642
643    #[tokio::test]
644    async fn test_execution_with_null() {
645        let ctx = Arc::new(TestContext {
646            map: HashMap::new(),
647        });
648        let res = evaluate_with_context(
649            r#"
650        {
651                    "variables": {
652                        "map": {
653                            "user": {
654                                "type": "map",
655                                "value": {
656                                    "some_value": {
657                                        "type": "Null",
658                                        "value": null
659                                    }
660                                }
661                            }
662                        }
663                    },
664                    "expression": "user.should_display == true && user.some_value > 12"
665       }
666
667        "#
668            .to_string(),
669            ctx,
670        );
671        println!("{}", res.clone());
672        assert_eq!(res, "{\"Err\":\"No such key: should_display\"}");
673    }
674    #[tokio::test]
675    async fn test_execution_with_platform_computed_reference() {
676        let days_since = PassableValue::UInt(7);
677        let days_since = serde_json::to_string(&days_since).unwrap();
678        let ctx = Arc::new(TestContext {
679            map: [("minutesSince".to_string(), days_since)]
680                .iter()
681                .cloned()
682                .collect(),
683        });
684        let res = evaluate_with_context(
685            r#"
686    {
687        "variables": {
688            "map": {}
689        },
690        "expression": "device.minutesSince('app_launch') == computed.minutesSince('app_install')",
691        "computed": {
692            "daysSince": [
693                {
694                    "type": "string",
695                    "value": "event_name"
696                }
697            ],
698            "minutesSince": [
699                {
700                    "type": "string",
701                    "value": "event_name"
702                }
703            ],
704            "hoursSince": [
705                {
706                    "type": "string",
707                    "value": "event_name"
708                }
709            ],
710            "monthsSince": [
711                {
712                    "type": "string",
713                    "value": "event_name"
714                }
715            ]
716        },
717        "device": {
718            "daysSince": [
719                {
720                    "type": "string",
721                    "value": "event_name"
722                }
723            ],
724            "minutesSince": [
725                {
726                    "type": "string",
727                    "value": "event_name"
728                }
729            ],
730            "hoursSince": [
731                {
732                    "type": "string",
733                    "value": "event_name"
734                }
735            ],
736            "monthsSince": [
737                {
738                    "type": "string",
739                    "value": "event_name"
740                }
741            ]
742        }
743    }"#
744            .to_string(),
745            ctx,
746        );
747        println!("{}", res.clone());
748        assert_eq!(res, "{\"Ok\":{\"type\":\"bool\",\"value\":true}}");
749    }
750
751    #[tokio::test]
752    async fn test_execution_with_platform_device_function_and_property() {
753        let days_since = PassableValue::UInt(7);
754        let days_since = serde_json::to_string(&days_since).unwrap();
755        let ctx = Arc::new(TestContext {
756            map: [("minutesSince".to_string(), days_since)]
757                .iter()
758                .cloned()
759                .collect(),
760        });
761        let res = evaluate_with_context(
762            r#"
763    {
764        "variables": {
765            "map": {
766                "device": {
767                    "type": "map",
768                    "value": {
769                        "trial_days": {
770                            "type": "uint",
771                            "value": 7
772                        }
773                    }
774                }
775            }
776        },
777        "expression": "computed.minutesSince('app_launch') == device.trial_days",
778        "computed": {
779            "daysSince": [
780                {
781                    "type": "string",
782                    "value": "event_name"
783                }
784            ],
785            "minutesSince": [
786                {
787                    "type": "string",
788                    "value": "event_name"
789                }
790            ],
791            "hoursSince": [
792                {
793                    "type": "string",
794                    "value": "event_name"
795                }
796            ],
797            "monthsSince": [
798                {
799                    "type": "string",
800                    "value": "event_name"
801                }
802            ]
803        },
804        "device": {
805            "daysSince": [
806                {
807                    "type": "string",
808                    "value": "event_name"
809                }
810            ],
811            "minutesSince": [
812                {
813                    "type": "string",
814                    "value": "event_name"
815                }
816            ],
817            "hoursSince": [
818                {
819                    "type": "string",
820                    "value": "event_name"
821                }
822            ],
823            "monthsSince": [
824                {
825                    "type": "string",
826                    "value": "event_name"
827                }
828            ]
829        }
830    }"#
831            .to_string(),
832            ctx,
833        );
834        println!("{}", res.clone());
835        assert_eq!(res, "{\"Ok\":{\"type\":\"bool\",\"value\":true}}");
836    }
837
838    #[test]
839    fn test_parse_to_ast() {
840        let expression = "device.daysSince(app_install) == 3";
841        let ast_json = parse_to_ast(expression.to_string());
842        println!("\nSerialized AST:");
843        println!("{}", ast_json);
844        // Deserialize back to JSONExpression
845        let deserialized_json_expr: JSONExpression = serde_json::from_str(&ast_json).unwrap();
846
847        // Convert back to original Expression
848        let deserialized_expr: Expression = deserialized_json_expr.into();
849
850        println!("\nDeserialized Expression:");
851        println!("{:?}", deserialized_expr);
852
853        let parsed_expression = parse(expression).unwrap();
854        assert_eq!(parsed_expression, deserialized_expr);
855        println!("\nOriginal and deserialized expressions are equal!");
856    }
857}