1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
extern crate tera;

use super::Updater;
use super::{Action, ActionType};
use crate::helper::json_pointer::JsonPointer;
use crate::updater::tera_helpers::{filters, function};
use json_value_merge::Merge;
use json_value_remove::Remove;
use json_value_resolve::Resolve;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::error::Error as StdError;
use std::{fmt, io};

#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[serde(default)]
pub struct Tera {}

impl fmt::Display for Tera {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Tera {{}}")
    }
}

impl Updater for Tera {
    fn update(
        &self,
        object: Value,
        mapping: Option<HashMap<String, Vec<Value>>>,
        actions: Vec<Action>,
        input_name: String,
        output_name: String,
    ) -> io::Result<Value> {
        trace!(input = format!("{}", object).as_str(), updater = format!("{}", self).as_str(), "Update");
        let mut engine = Tera::engine();
        let mut context = tera::Context::new();
        context.insert(input_name, &object);

        if let Some(mapping) = mapping {
            for (field_path, object) in mapping {
                context.insert(&field_path.clone(), &object.clone());
            }
        }

        let mut json_value = Value::default();
        for action in actions {
            trace!(field = action.field.as_str(), "Field fetch into the pattern collection");
            context.insert(output_name.clone(), &json_value.clone());

            let mut field_new_value = Value::default();

            match &action.pattern {
                Some(pattern) => {
                    let render_result: String = match engine.render_str(pattern.as_str(), &context)
                    {
                        Ok(render_result) => Ok(render_result),
                        Err(e) => Err(io::Error::new(
                            io::ErrorKind::InvalidInput,
                            format!(
                                "Failed to render the field '{}'. {}.",
                                action.field,
                                match e.source() {
                                    Some(e) => {
                                        match e.source() {
                                            Some(e) => {
                                                e.to_string()
                                            }
                                            None => e.to_string(),
                                        }
                                    }
                                    None => format!("Please fix the pattern `{}`", pattern.to_string()),
                                }.replace(" '__tera_one_off'", "")
                            ),
                        )),
                    }?;
                    trace!(value = render_result.as_str(),  "Field value before resolved it");
                    field_new_value = Value::resolve(render_result);
                    trace!(value = format!("{}", field_new_value).as_str(),  "Field value after resolved it");
                }
                None => (),
            };

            let json_pointer = action.field.clone().to_json_pointer();

            trace!(output = format!("{}", json_value).as_str(),
                jpointer = json_pointer.to_string().as_str(),
                data = format!("{}", field_new_value).as_str(),
                "{} the new field", action.action_type
            );

            match action.action_type {
                ActionType::Merge => {
                    json_value.merge_in(&json_pointer, field_new_value)?;
                }
                ActionType::Replace => {
                    json_value.merge_in(&json_pointer, Value::Null)?;
                    json_value.merge_in(&json_pointer, field_new_value)?;
                }
                ActionType::Remove => {
                    json_value.remove(&json_pointer)?;
                }
            }
        }

        trace!(output = format!("{}", json_value).as_str(),  "Update ended");
        Ok(json_value)
    }
}

impl Tera {
    fn engine() -> tera::Tera {
        let mut engine = tera::Tera::default();

        engine.autoescape_on(vec![]);
        // register new filter
        engine.register_filter("merge", filters::object::merge);
        engine.register_function("uuid_v4", function::uuid_v4);
        engine.register_function("set_env", function::set_env);
        engine.register_function("base64_encode", function::base64_encode);
        engine.register_function("base64_decode", function::base64_decode);
        engine.register_filter("search", filters::object::search);

        engine
    }
}