use super::{get_param, Mutator, MutatorParams};
use crate::error::{Result, TqlError};
use serde_json::Value as JsonValue;
pub struct LowercaseMutator {
_params: MutatorParams,
}
impl LowercaseMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
}
impl Mutator for LowercaseMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::String(s) => Ok(JsonValue::String(s.to_lowercase())),
JsonValue::Array(arr) => {
let transformed: Vec<JsonValue> = arr
.iter()
.map(|item| {
if let JsonValue::String(s) = item {
JsonValue::String(s.to_lowercase())
} else {
item.clone()
}
})
.collect();
Ok(JsonValue::Array(transformed))
}
_ => Ok(value.clone()),
}
}
fn name(&self) -> &str {
"lowercase"
}
}
pub struct UppercaseMutator {
_params: MutatorParams,
}
impl UppercaseMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
}
impl Mutator for UppercaseMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::String(s) => Ok(JsonValue::String(s.to_uppercase())),
JsonValue::Array(arr) => {
let transformed: Vec<JsonValue> = arr
.iter()
.map(|item| {
if let JsonValue::String(s) = item {
JsonValue::String(s.to_uppercase())
} else {
item.clone()
}
})
.collect();
Ok(JsonValue::Array(transformed))
}
_ => Ok(value.clone()),
}
}
fn name(&self) -> &str {
"uppercase"
}
}
pub struct TrimMutator {
_params: MutatorParams,
}
impl TrimMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
}
impl Mutator for TrimMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::String(s) => Ok(JsonValue::String(s.trim().to_string())),
JsonValue::Array(arr) => {
let transformed: Vec<JsonValue> = arr
.iter()
.map(|item| {
if let JsonValue::String(s) = item {
JsonValue::String(s.trim().to_string())
} else {
item.clone()
}
})
.collect();
Ok(JsonValue::Array(transformed))
}
_ => Ok(value.clone()),
}
}
fn name(&self) -> &str {
"trim"
}
}
pub struct SplitMutator {
params: MutatorParams,
}
impl SplitMutator {
pub fn new(params: MutatorParams) -> Self {
Self { params }
}
fn get_delimiter(&self) -> String {
get_param(&self.params, "delimiter", 0)
.and_then(|v| v.as_str())
.unwrap_or(" ")
.to_string()
}
}
impl Mutator for SplitMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
let delimiter = self.get_delimiter();
match value {
JsonValue::String(s) => {
let parts: Vec<JsonValue> = s
.split(&delimiter)
.map(|part| JsonValue::String(part.to_string()))
.collect();
Ok(JsonValue::Array(parts))
}
JsonValue::Array(arr) => {
let transformed: Vec<JsonValue> = arr
.iter()
.flat_map(|item| {
if let JsonValue::String(s) = item {
s.split(&delimiter)
.map(|part| JsonValue::String(part.to_string()))
.collect::<Vec<JsonValue>>()
} else {
vec![item.clone()]
}
})
.collect();
Ok(JsonValue::Array(transformed))
}
_ => Ok(value.clone()),
}
}
fn name(&self) -> &str {
"split"
}
}
pub struct LengthMutator {
_params: MutatorParams,
}
impl LengthMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
}
impl Mutator for LengthMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::String(s) => Ok(JsonValue::Number(serde_json::Number::from(
s.chars().count(),
))),
JsonValue::Array(arr) => Ok(JsonValue::Number(serde_json::Number::from(arr.len()))),
_ => Ok(JsonValue::Number(serde_json::Number::from(0))),
}
}
fn name(&self) -> &str {
"length"
}
}
pub struct ReplaceMutator {
params: MutatorParams,
}
impl ReplaceMutator {
pub fn new(params: MutatorParams) -> Self {
Self { params }
}
fn get_find(&self) -> Result<String> {
get_param(&self.params, "find", 0)
.and_then(|v| v.as_str())
.map(|s| s.to_string())
.ok_or_else(|| {
TqlError::MutatorError("Replace mutator requires 'find' parameter".to_string())
})
}
fn get_replace(&self) -> String {
get_param(&self.params, "replace", 1)
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string()
}
}
impl Mutator for ReplaceMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
let find = self.get_find()?;
let replace = self.get_replace();
match value {
JsonValue::String(s) => Ok(JsonValue::String(s.replace(&find, &replace))),
JsonValue::Array(arr) => {
let transformed: Result<Vec<JsonValue>> = arr
.iter()
.map(|item| {
if let JsonValue::String(s) = item {
Ok(JsonValue::String(s.replace(&find, &replace)))
} else {
Ok(item.clone())
}
})
.collect();
Ok(JsonValue::Array(transformed?))
}
_ => Ok(value.clone()),
}
}
fn name(&self) -> &str {
"replace"
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
use std::collections::HashMap;
#[test]
fn test_lowercase_mutator() {
let mutator = LowercaseMutator::new(HashMap::new());
let record = json!({});
let value = json!("HELLO WORLD");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("hello world"));
let value = json!(["HELLO", "WORLD"]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(["hello", "world"]));
let value = json!(42);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(42));
}
#[test]
fn test_uppercase_mutator() {
let mutator = UppercaseMutator::new(HashMap::new());
let record = json!({});
let value = json!("hello world");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("HELLO WORLD"));
}
#[test]
fn test_trim_mutator() {
let mutator = TrimMutator::new(HashMap::new());
let record = json!({});
let value = json!(" hello world ");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("hello world"));
let value = json!([" hello ", " world "]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(["hello", "world"]));
}
#[test]
fn test_split_mutator() {
let mut params = HashMap::new();
params.insert("delimiter".to_string(), json!(","));
let mutator = SplitMutator::new(params);
let record = json!({});
let value = json!("a,b,c");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(["a", "b", "c"]));
}
#[test]
fn test_split_default_delimiter() {
let mutator = SplitMutator::new(HashMap::new());
let record = json!({});
let value = json!("hello world");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(["hello", "world"]));
}
#[test]
fn test_length_mutator() {
let mutator = LengthMutator::new(HashMap::new());
let record = json!({});
let value = json!("hello");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(5));
let value = json!(["a", "b", "c"]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(3));
}
#[test]
fn test_replace_mutator() {
let mut params = HashMap::new();
params.insert("find".to_string(), json!("world"));
params.insert("replace".to_string(), json!("universe"));
let mutator = ReplaceMutator::new(params);
let record = json!({});
let value = json!("hello world");
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!("hello universe"));
let value = json!(["hello world", "world peace"]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(["hello universe", "universe peace"]));
}
#[test]
fn test_replace_missing_find_parameter() {
let mutator = ReplaceMutator::new(HashMap::new());
let record = json!({});
let value = json!("hello");
let result = mutator.apply("field", &record, &value);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Replace mutator requires 'find' parameter"));
}
}