#![deny(missing_docs)]
use serde_json::{json, Value};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Type {
Int,
Float,
Bool,
String,
}
pub fn coerce_one(v: Value, ty: Type) -> Option<Value> {
let v = if let Value::Array(ref arr) = v {
if arr.len() == 1 {
arr[0].clone()
} else {
v
}
} else {
v
};
match ty {
Type::Int => match v {
Value::Number(n) => n.as_i64().map(|i| json!(i)),
Value::String(s) => s.trim().parse::<i64>().ok().map(|i| json!(i)),
Value::Bool(b) => Some(json!(if b { 1 } else { 0 })),
_ => None,
},
Type::Float => match v {
Value::Number(n) => n.as_f64().map(|f| json!(f)),
Value::String(s) => s.trim().parse::<f64>().ok().map(|f| json!(f)),
_ => None,
},
Type::Bool => match v {
Value::Bool(b) => Some(json!(b)),
Value::String(s) => match s.trim().to_ascii_lowercase().as_str() {
"true" | "yes" | "y" | "1" => Some(json!(true)),
"false" | "no" | "n" | "0" => Some(json!(false)),
_ => None,
},
Value::Number(n) => n.as_i64().map(|i| json!(i != 0)),
_ => None,
},
Type::String => Some(match v {
Value::String(s) => json!(s),
Value::Number(n) => json!(n.to_string()),
Value::Bool(b) => json!(b.to_string()),
Value::Null => json!(""),
_ => json!(v.to_string()),
}),
}
}
pub fn coerce_args(mut args: Value, schema: &[(&str, Type)]) -> Value {
if let Value::Object(map) = &mut args {
for (name, ty) in schema {
if let Some(slot) = map.remove(*name) {
if let Some(fixed) = coerce_one(slot.clone(), *ty) {
map.insert((*name).to_string(), fixed);
} else {
map.insert((*name).to_string(), slot);
}
}
}
}
args
}