reactive_graph_std_string/behaviour/entity/
templating.rs

1use log::error;
2use reactive_graph_behaviour_model_api::behaviour_validator;
3use reactive_graph_behaviour_model_api::prelude::*;
4use reactive_graph_behaviour_model_impl::entity_behaviour;
5use reactive_graph_graph::prelude::*;
6use reactive_graph_reactive_model_impl::ReactiveEntity;
7use serde_json::Value;
8use serde_json::json;
9use tera::Context;
10use tera::Tera;
11use uuid::Uuid;
12
13use reactive_graph_std_result_model::ResultStringProperties::RESULT;
14use reactive_graph_std_string_model::TemplatingProperties::CONTEXT;
15use reactive_graph_std_string_model::TemplatingProperties::TEMPLATE;
16
17entity_behaviour!(Templating, TemplatingFactory, TemplatingFsm, TemplatingBehaviourTransitions, TemplatingValidator);
18
19behaviour_validator!(TemplatingValidator, Uuid, ReactiveEntity, TEMPLATE.as_ref(), CONTEXT.as_ref(), RESULT.as_ref());
20
21impl BehaviourInit<Uuid, ReactiveEntity> for TemplatingBehaviourTransitions {
22    fn init(&self) -> Result<(), BehaviourInitializationFailed> {
23        let template = self.reactive_instance.get(TEMPLATE).ok_or(BehaviourInitializationFailed {})?;
24        let context = self.reactive_instance.get(CONTEXT).ok_or(BehaviourInitializationFailed {})?;
25        self.reactive_instance.set(RESULT, render(&template, &context));
26        Ok(())
27    }
28}
29
30impl BehaviourConnect<Uuid, ReactiveEntity> for TemplatingBehaviourTransitions {
31    fn connect(&self) -> Result<(), BehaviourConnectFailed> {
32        let reactive_instance = self.reactive_instance.clone();
33        self.property_observers.observe_with_handle(CONTEXT.as_ref(), move |context: &Value| {
34            if let Some(template) = reactive_instance.get(TEMPLATE) {
35                reactive_instance.set(RESULT, render(&template, context));
36            }
37        });
38        let reactive_instance = self.reactive_instance.clone();
39        self.property_observers.observe_with_handle(TEMPLATE.as_ref(), move |template: &Value| {
40            if let Some(context) = reactive_instance.get(CONTEXT) {
41                reactive_instance.set(RESULT, render(template, &context));
42            }
43        });
44        Ok(())
45    }
46}
47
48impl BehaviourShutdown<Uuid, ReactiveEntity> for TemplatingBehaviourTransitions {}
49impl BehaviourTransitions<Uuid, ReactiveEntity> for TemplatingBehaviourTransitions {}
50
51fn render(template: &Value, context: &Value) -> Value {
52    match template.as_str() {
53        Some(template) => match Context::from_value(context.clone()) {
54            Ok(tera_context) => {
55                let mut tera = Tera::default();
56                tera.autoescape_on(vec![]);
57                match tera.render_str(template, &tera_context) {
58                    Ok(rendered) => json!(rendered),
59                    Err(e) => {
60                        error!("Rendering Error: {}", e);
61                        json!({
62                            "error": "Rendering Error",
63                            "message": format!("{}", e),
64                            "template": template,
65                            "context": context
66                        })
67                    }
68                }
69            }
70            Err(e) => {
71                error!("Rendering Error: {}", e);
72                json!({
73                    "error": "Context Error",
74                    "message": format!("{}", e),
75                    "template": template,
76                    "context": context
77                })
78            }
79        },
80        None => {
81            json!({
82                "error": "Template Error",
83                "message": "Template not a string",
84                "template": template,
85                "context": context
86            })
87        }
88    }
89}