datamorph_rs/
lib.rs

1use std::collections::HashMap;
2use serde::Deserialize;
3use serde_json::{Value, Map};
4use jsonlogic_rs::apply;
5
6mod error;
7pub use error::Error;
8
9#[derive(Debug, Deserialize)]
10#[serde(tag = "type")]
11pub enum TransformType {
12    #[serde(rename = "field")]
13    Field {
14        source: String,
15        target: String,
16        #[serde(default)]
17        transform: Option<String>,
18    },
19    #[serde(rename = "concat")]
20    Concat {
21        sources: Vec<String>,
22        target: String,
23        #[serde(default)]
24        separator: Option<String>,
25    },
26    #[serde(rename = "split")]
27    Split {
28        source: String,
29        targets: HashMap<String, SplitTarget>,
30        #[serde(default)]
31        separator: Option<String>,
32    },
33}
34
35#[derive(Debug, Deserialize)]
36pub struct Transform {
37    #[serde(flatten)]
38    pub transform_type: TransformType,
39    #[serde(default)]
40    pub condition: Option<Value>,
41}
42
43#[derive(Debug, Deserialize)]
44pub struct SplitTarget {
45    pub index: usize,
46    #[serde(default)]
47    pub transform: Option<String>,
48}
49
50pub struct Datamorph {
51    transforms: Vec<Transform>,
52}
53
54impl Datamorph {
55    pub fn from_json(spec: &str) -> Result<Self, Error> {
56        let transforms: Vec<Transform> = serde_json::from_str(spec)?;
57        Ok(Self { transforms })
58    }
59
60    pub fn transform<T, U>(&self, input: T) -> Result<U, Error>
61    where
62        T: serde::Serialize,
63        U: for<'de> serde::Deserialize<'de>,
64    {
65        let input = serde_json::to_value(input)?;
66        let mut output = Value::Object(Map::new());
67
68        for transform in &self.transforms {
69            if let Some(result) = transform.apply(&input)? {
70                self.merge_values(&mut output, result);
71            }
72        }
73
74        Ok(serde_json::from_value(output)?)
75    }
76
77    fn merge_values(&self, target: &mut Value, source: Value) {
78        if let (Some(target_obj), Some(source_obj)) = (target.as_object_mut(), source.as_object()) {
79            for (key, value) in source_obj {
80                match target_obj.get_mut(key.as_str()) {  // Using as_str() for String
81                    Some(existing) => self.merge_values(existing, value.clone()),
82                    None => { target_obj.insert(key.clone(), value.clone()); }
83                }
84            }
85        }
86    }
87}
88
89impl Transform {
90    pub fn apply(&self, input: &Value) -> Result<Option<Value>, Error> {
91        if !self.check_condition(input)? {
92            return Ok(None);
93        }
94
95        match &self.transform_type {
96            TransformType::Field { source, target, transform } => {
97                let value = self.get_value(input, source)?;
98                let transformed = self.apply_transform(value.clone(), transform)?;
99                Ok(Some(self.set_value_at_path(target, transformed)))
100            },
101            TransformType::Concat { sources, target, separator } => {
102                let values: Vec<String> = sources.iter()
103                    .filter_map(|s| self.get_value(input, s).ok())
104                    .filter_map(|v| v.as_str().map(String::from))
105                    .collect();
106                
107                let result = values.join(separator.as_deref().unwrap_or(""));
108                Ok(Some(self.set_value_at_path(target, Value::String(result))))
109            },
110            TransformType::Split { source, targets, separator } => {
111                let value = self.get_value(input, source)?;
112                let str_value = value.as_str()
113                    .ok_or_else(|| Error::Transform("Source must be string".into()))?;
114                
115                let parts: Vec<&str> = str_value.split(separator.as_deref().unwrap_or(""))
116                    .collect();
117                
118                let mut result = Map::new();
119                for (target_path, split_target) in targets {
120                    if let Some(part) = parts.get(split_target.index) {
121                        let value = self.apply_transform(
122                            Value::String(part.to_string()),
123                            &split_target.transform
124                        )?;
125                        result.insert(target_path.clone(), value);
126                    }
127                }
128                
129                Ok(Some(Value::Object(result)))
130            },
131        }
132    }
133
134    fn check_condition(&self, input: &Value) -> Result<bool, Error> {
135        match &self.condition {
136            Some(condition) => {
137                apply(condition, input)
138                    .map_err(|e| Error::Logic(e.to_string()))?
139                    .as_bool()
140                    .ok_or_else(|| Error::Logic("Condition must evaluate to boolean".into()))
141            },
142            None => Ok(true),
143        }
144    }
145
146    fn get_value<'a>(&self, input: &'a Value, path: &str) -> Result<&'a Value, Error> {
147        path.split('.')
148            .try_fold(input, |value, key| {
149                value.get(key).ok_or_else(|| Error::MissingField(path.to_string()))
150            })
151    }
152
153    fn apply_transform(&self, value: Value, transform: &Option<String>) -> Result<Value, Error> {
154        match transform {
155            Some(transform) => match transform.as_str() {
156                "uppercase" => Ok(Value::String(
157                    value.as_str()
158                        .ok_or_else(|| Error::Transform("Expected string".into()))?
159                        .to_uppercase()
160                )),
161                "lowercase" => Ok(Value::String(
162                    value.as_str()
163                        .ok_or_else(|| Error::Transform("Expected string".into()))?
164                        .to_lowercase()
165                )),
166                _ => Err(Error::Transform(format!("Unknown transform: {}", transform))),
167            },
168            None => Ok(value),
169        }
170    }
171
172    fn set_value_at_path(&self, path: &str, value: Value) -> Value {
173        let parts: Vec<&str> = path.split('.').collect();
174        let mut result = Map::new();
175        let mut current = &mut result;
176
177        for (i, part) in parts.iter().enumerate() {
178            if i == parts.len() - 1 {
179                current.insert((*part).to_string(), value.clone());
180            } else {
181                current.insert((*part).to_string(), Value::Object(Map::new()));
182                current = current.get_mut(*part)  // Using *part directly as it's already &str
183                    .unwrap()
184                    .as_object_mut()
185                    .unwrap();
186            }
187        }
188
189        Value::Object(result)
190    }
191}