schema-coerce 0.1.0

Coerce LLM JSON values to a simple field-schema: string->int, bool, float; strip wrapper objects; fill defaults. Forgiving structured-output recovery.
Documentation
use schema_coerce::{coerce, Field, Type};
use serde_json::json;

#[test]
fn coerces_string_to_int() {
    let schema = vec![Field { name: "n", ty: Type::Int, default: None }];
    let out = coerce(json!({"n": "42"}), &schema);
    assert_eq!(out["n"], json!(42));
}

#[test]
fn coerces_string_to_bool() {
    let schema = vec![Field { name: "ok", ty: Type::Bool, default: None }];
    assert_eq!(coerce(json!({"ok": "yes"}), &schema)["ok"], json!(true));
    assert_eq!(coerce(json!({"ok": "false"}), &schema)["ok"], json!(false));
    assert_eq!(coerce(json!({"ok": "Y"}), &schema)["ok"], json!(true));
}

#[test]
fn unwraps_single_key_wrapper() {
    let schema = vec![Field { name: "x", ty: Type::Int, default: None }];
    let wrapped = json!({"result": {"x": 7}});
    assert_eq!(coerce(wrapped, &schema)["x"], json!(7));
}

#[test]
fn fills_defaults_for_missing_fields() {
    let schema = vec![
        Field { name: "x", ty: Type::Int, default: Some(json!(0)) },
        Field { name: "y", ty: Type::String, default: Some(json!("z")) },
    ];
    let out = coerce(json!({}), &schema);
    assert_eq!(out["x"], json!(0));
    assert_eq!(out["y"], json!("z"));
}

#[test]
fn returns_null_for_uncoercible_no_default() {
    let schema = vec![Field { name: "n", ty: Type::Int, default: None }];
    let out = coerce(json!({"n": "not-a-number"}), &schema);
    assert_eq!(out["n"], json!(null));
}

#[test]
fn passes_through_correct_types() {
    let schema = vec![Field { name: "a", ty: Type::Array, default: None }];
    let out = coerce(json!({"a": [1, 2, 3]}), &schema);
    assert_eq!(out["a"], json!([1, 2, 3]));
}

#[test]
fn float_string_to_float() {
    let schema = vec![Field { name: "x", ty: Type::Float, default: None }];
    let out = coerce(json!({"x": "3.14"}), &schema);
    assert_eq!(out["x"], json!(3.14));
}