use super::{Mutator, MutatorParams};
use crate::error::Result;
use serde_json::{json, Value as JsonValue};
pub struct AnyMutator {
_params: MutatorParams,
}
impl AnyMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
}
impl Mutator for AnyMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::Null => Ok(json!(false)),
JsonValue::Array(arr) => {
let result = arr.iter().any(is_truthy);
Ok(json!(result))
}
_ => Ok(json!(is_truthy(value))),
}
}
fn name(&self) -> &str {
"any"
}
}
pub struct AllMutator {
_params: MutatorParams,
}
impl AllMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
}
impl Mutator for AllMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::Null => Ok(json!(false)),
JsonValue::Array(arr) => {
let result = arr.iter().all(is_truthy);
Ok(json!(result))
}
_ => Ok(json!(is_truthy(value))),
}
}
fn name(&self) -> &str {
"all"
}
}
pub struct AvgMutator {
_params: MutatorParams,
}
impl AvgMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
}
impl Mutator for AvgMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::Null => Ok(JsonValue::Null),
JsonValue::Array(arr) => {
let numeric_values: Vec<f64> = arr.iter().filter_map(to_numeric).collect();
if numeric_values.is_empty() {
Ok(JsonValue::Null)
} else {
let avg = numeric_values.iter().sum::<f64>() / numeric_values.len() as f64;
Ok(json!(avg))
}
}
_ => {
if let Some(num) = to_numeric(value) {
Ok(json!(num))
} else {
Ok(JsonValue::Null)
}
}
}
}
fn name(&self) -> &str {
"avg"
}
}
pub struct AverageMutator {
params: MutatorParams,
}
impl AverageMutator {
pub fn new(params: MutatorParams) -> Self {
Self { params }
}
}
impl Mutator for AverageMutator {
fn apply(&self, field_name: &str, record: &JsonValue, value: &JsonValue) -> Result<JsonValue> {
let avg_mutator = AvgMutator::new(self.params.clone());
avg_mutator.apply(field_name, record, value)
}
fn name(&self) -> &str {
"average"
}
}
pub struct SumMutator {
_params: MutatorParams,
}
impl SumMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
}
impl Mutator for SumMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::Null => Ok(json!(0.0)),
JsonValue::Array(arr) => {
let sum: f64 = arr.iter().filter_map(to_numeric).sum();
Ok(json!(sum))
}
_ => {
if let Some(num) = to_numeric(value) {
Ok(json!(num))
} else {
Ok(json!(0.0))
}
}
}
}
fn name(&self) -> &str {
"sum"
}
}
pub struct MaxMutator {
_params: MutatorParams,
}
impl MaxMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
}
impl Mutator for MaxMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::Null => Ok(JsonValue::Null),
JsonValue::Array(arr) => {
let max = arr
.iter()
.filter_map(to_numeric)
.max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
if let Some(max_val) = max {
Ok(json!(max_val))
} else {
Ok(JsonValue::Null)
}
}
_ => {
if let Some(num) = to_numeric(value) {
Ok(json!(num))
} else {
Ok(JsonValue::Null)
}
}
}
}
fn name(&self) -> &str {
"max"
}
}
pub struct MinMutator {
_params: MutatorParams,
}
impl MinMutator {
pub fn new(params: MutatorParams) -> Self {
Self { _params: params }
}
}
impl Mutator for MinMutator {
fn apply(
&self,
_field_name: &str,
_record: &JsonValue,
value: &JsonValue,
) -> Result<JsonValue> {
match value {
JsonValue::Null => Ok(JsonValue::Null),
JsonValue::Array(arr) => {
let min = arr
.iter()
.filter_map(to_numeric)
.min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
if let Some(min_val) = min {
Ok(json!(min_val))
} else {
Ok(JsonValue::Null)
}
}
_ => {
if let Some(num) = to_numeric(value) {
Ok(json!(num))
} else {
Ok(JsonValue::Null)
}
}
}
}
fn name(&self) -> &str {
"min"
}
}
fn is_truthy(value: &JsonValue) -> bool {
match value {
JsonValue::Null => false,
JsonValue::Bool(b) => *b,
JsonValue::Number(n) => n.as_f64().unwrap_or(0.0) != 0.0,
JsonValue::String(s) => !s.is_empty(),
JsonValue::Array(arr) => !arr.is_empty(),
JsonValue::Object(obj) => !obj.is_empty(),
}
}
fn to_numeric(value: &JsonValue) -> Option<f64> {
match value {
JsonValue::Number(n) => n.as_f64(),
JsonValue::String(s) => s.parse::<f64>().ok(),
JsonValue::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
use std::collections::HashMap;
#[test]
fn test_any_mutator() {
let mutator = AnyMutator::new(HashMap::new());
let record = json!({});
let value = json!([true, false, true]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(true));
let value = json!([false, 0, ""]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(false));
let value = json!(42);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(true));
let value = JsonValue::Null;
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(false));
}
#[test]
fn test_all_mutator() {
let mutator = AllMutator::new(HashMap::new());
let record = json!({});
let value = json!([true, 1, "hello"]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(true));
let value = json!([true, false, true]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(false));
let value = json!([]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(true));
}
#[test]
fn test_avg_mutator() {
let mutator = AvgMutator::new(HashMap::new());
let record = json!({});
let value = json!([10, 20, 30]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(20.0));
let value = json!([10, "20", 30]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(20.0));
let value = json!(42);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(42.0));
let value = json!([]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, JsonValue::Null);
}
#[test]
fn test_sum_mutator() {
let mutator = SumMutator::new(HashMap::new());
let record = json!({});
let value = json!([10, 20, 30]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(60.0));
let value = json!(42);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(42.0));
let value = JsonValue::Null;
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(0.0));
}
#[test]
fn test_max_mutator() {
let mutator = MaxMutator::new(HashMap::new());
let record = json!({});
let value = json!([10, 50, 30]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(50.0));
let value = json!(42);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(42.0));
}
#[test]
fn test_min_mutator() {
let mutator = MinMutator::new(HashMap::new());
let record = json!({});
let value = json!([10, 50, 30]);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(10.0));
let value = json!(42);
let result = mutator.apply("field", &record, &value).unwrap();
assert_eq!(result, json!(42.0));
}
}