ttj 1.1.4

Serialize OpenType tables to JSON
Documentation
use indexmap::IndexSet;
use serde_json::{json, Map, Value};

pub trait Substantial {
    fn is_something(&self) -> bool;
}
impl Substantial for Value {
    fn is_something(&self) -> bool {
        match self {
            Value::Null => false,
            Value::Bool(x) => *x,
            Value::Number(x) => x.as_f64().unwrap_or(0.0).abs() > f64::EPSILON,
            Value::String(x) => !x.is_empty(),
            Value::Array(x) => !x.is_empty(),
            Value::Object(x) => !x.is_empty(),
        }
    }
}

pub fn diff(this: &Value, other: &Value, max_changes: usize) -> Value {
    match (this, other) {
        (Value::Null, Value::Null) => Value::Null,
        (Value::Number(l), Value::Number(r)) => {
            if l == r {
                Value::Null
            } else {
                Value::Array(vec![this.clone(), other.clone()])
            }
        }
        (Value::Bool(l), Value::Bool(r)) => {
            if l == r {
                Value::Null
            } else {
                Value::Array(vec![this.clone(), other.clone()])
            }
        }
        (Value::String(l), Value::String(r)) => {
            if l == r {
                Value::Null
            } else {
                Value::Array(vec![this.clone(), other.clone()])
            }
        }
        (Value::Array(l), Value::Array(r)) => {
            let mut res = Map::new();
            for i in 0..(l.len().max(r.len())) {
                let difference = diff(
                    l.get(i).unwrap_or(&Value::Null),
                    r.get(i).unwrap_or(&Value::Null),
                    max_changes,
                );
                if difference.is_something() {
                    res.insert(i.to_string(), difference);
                }
            }
            if res.len() > max_changes && max_changes > 0 {
                json!({ "error": format!("There are {} changes, check manually!", res.len()) })
            } else {
                Value::Object(res)
            }
        }
        (Value::Object(l), Value::Object(r)) => {
            let mut res = Map::new();
            let mut all_keys = IndexSet::new();
            all_keys.extend(l.keys());
            all_keys.extend(r.keys());
            for key in all_keys {
                let difference = diff(
                    l.get(key).unwrap_or(&Value::Null),
                    r.get(key).unwrap_or(&Value::Null),
                    max_changes,
                );
                if difference.is_something() {
                    res.insert(key.to_string(), difference);
                }
            }
            if res.is_empty() {
                Value::Null
            } else if res.len() > max_changes && max_changes > 0 {
                json!({ "error": format!("There are {} changes, check manually!", res.len()) })
            } else {
                Value::Object(res)
            }
        }
        (_, _) => Value::Array(vec![this.clone(), other.clone()]),
    }
}