use serde_json::Value;
use crate::error::{IssueCode, PathSegment, VldError};
use crate::schema::VldParse;
pub fn parse_coerced<T: VldParse>(value: &Value) -> Result<T, VldError> {
let mut err = match T::vld_parse_value(value) {
Ok(v) => return Ok(v),
Err(e) => e,
};
let mut current = value.clone();
for _ in 0..4 {
if !repair(&mut current, &err) {
break;
}
match T::vld_parse_value(¤t) {
Ok(v) => return Ok(v),
Err(e) => err = e,
}
}
Err(err)
}
fn repair(value: &mut Value, err: &VldError) -> bool {
let mut changed = false;
for issue in &err.issues {
let IssueCode::InvalidType { expected, .. } = &issue.code else {
continue;
};
let Some(leaf) = pointer_mut(value, &issue.path) else {
continue;
};
match (expected.as_str(), &*leaf) {
("string", Value::Number(_) | Value::Bool(_)) => {
*leaf = Value::String(uncoerce(leaf));
changed = true;
}
("array", Value::String(_) | Value::Number(_) | Value::Bool(_)) => {
*leaf = Value::Array(vec![leaf.take()]);
changed = true;
}
_ => {}
}
}
changed
}
fn uncoerce(v: &Value) -> String {
match v {
Value::Number(n) => match (n.as_i64(), n.as_u64(), n.as_f64()) {
(Some(i), _, _) => i.to_string(),
(_, Some(u), _) => u.to_string(),
(_, _, Some(f)) => f.to_string(),
_ => n.to_string(),
},
Value::Bool(b) => b.to_string(),
other => other.to_string(),
}
}
fn pointer_mut<'a>(mut value: &'a mut Value, path: &[PathSegment]) -> Option<&'a mut Value> {
for seg in path {
value = match seg {
PathSegment::Field(k) => value.as_object_mut()?.get_mut(k)?,
PathSegment::Index(i) => value.as_array_mut()?.get_mut(*i)?,
};
}
Some(value)
}