midiserde 0.1.1

When mini isn't enough and serde is too much
Documentation
//! Tests for `#[mini(default)]` and combinations with rename/with.
//!
//! Run: `cargo test -p midiserde --features full default`

use midiserde::{json, Deserialize, Serialize};

#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct WithDefaults {
    name: String,
    #[mini(default)]
    retries: u32,
    #[mini(default)]
    timeout_ms: u64,
}

#[test]
fn missing_fields() {
    let j = r#"{"name": "partial"}"#;
    let v: WithDefaults = json::from_str(j).unwrap();
    assert_eq!(v.name, "partial");
    assert_eq!(v.retries, 0);
    assert_eq!(v.timeout_ms, 0);
}

#[test]
fn all_fields() {
    let j = r#"{"name": "full", "retries": 3, "timeout_ms": 5000}"#;
    let v: WithDefaults = json::from_str(j).unwrap();
    assert_eq!(v.name, "full");
    assert_eq!(v.retries, 3);
    assert_eq!(v.timeout_ms, 5000);
}

// ── custom default function ──

fn default_timeout_ms() -> u64 {
    5000
}

#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct WithCustomDefault {
    name: String,
    #[mini(default)]
    retries: u32,
    #[mini(default = "default_timeout_ms")]
    timeout_ms: u64,
}

#[test]
fn custom_default_missing() {
    let j = r#"{"name": "partial"}"#;
    let v: WithCustomDefault = json::from_str(j).unwrap();
    assert_eq!(v.name, "partial");
    assert_eq!(v.retries, 0);
    assert_eq!(v.timeout_ms, 5000);
}

#[test]
fn custom_default_present() {
    let j = r#"{"name": "full", "retries": 3, "timeout_ms": 1000}"#;
    let v: WithCustomDefault = json::from_str(j).unwrap();
    assert_eq!(v.name, "full");
    assert_eq!(v.retries, 3);
    assert_eq!(v.timeout_ms, 1000);
}

// ── with + default ──

#[cfg(feature = "base64")]
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct WithBase64AndDefault {
    name: String,
    #[mini(with = "midiserde::with::base64", default)]
    payload: Vec<u8>,
}

#[cfg(feature = "base64")]
#[test]
fn with_default_missing() {
    let j = r#"{"name": "no-payload"}"#;
    let v: WithBase64AndDefault = json::from_str(j).unwrap();
    assert_eq!(v.name, "no-payload");
    assert_eq!(v.payload, Vec::<u8>::new());
}

#[cfg(feature = "base64")]
#[test]
fn with_default_present() {
    let j = r#"{"name": "has-payload", "payload": "AQID"}"#;
    let v: WithBase64AndDefault = json::from_str(j).unwrap();
    assert_eq!(v.name, "has-payload");
    assert_eq!(v.payload, vec![1, 2, 3]);
}

// ── default + rename ──

#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct WithDefaultAndRename {
    #[mini(rename = "display_name")]
    display_name: String,
    #[mini(rename = "retry_count", default)]
    retry_count: u32,
}

#[test]
fn default_rename_missing_field() {
    let j = r#"{"display_name": "only-name"}"#;
    let v: WithDefaultAndRename = json::from_str(j).unwrap();
    assert_eq!(v.display_name, "only-name");
    assert_eq!(v.retry_count, 0);
}

#[test]
fn default_rename_all_fields() {
    let j = r#"{"display_name": "full", "retry_count": 5}"#;
    let v: WithDefaultAndRename = json::from_str(j).unwrap();
    assert_eq!(v.display_name, "full");
    assert_eq!(v.retry_count, 5);
}

#[test]
fn default_rename_roundtrip() {
    let original = WithDefaultAndRename {
        display_name: "test".into(),
        retry_count: 3,
    };
    let j = json::to_string(&original);
    let parsed: WithDefaultAndRename = json::from_str(&j).unwrap();
    assert_eq!(original, parsed);
}

// ── default + with + rename (all three) ──

#[cfg(feature = "base64")]
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct WithAllThree {
    #[mini(rename = "id")]
    id: String,
    #[mini(rename = "raw", with = "midiserde::with::base64", default)]
    raw: Vec<u8>,
}

#[cfg(feature = "base64")]
#[test]
fn all_three_missing() {
    let j = r#"{"id": "abc"}"#;
    let v: WithAllThree = json::from_str(j).unwrap();
    assert_eq!(v.id, "abc");
    assert_eq!(v.raw, Vec::<u8>::new());
}

#[cfg(feature = "base64")]
#[test]
fn all_three_present() {
    let j = r#"{"id": "xyz", "raw": "SGVsbG8="}"#;
    let v: WithAllThree = json::from_str(j).unwrap();
    assert_eq!(v.id, "xyz");
    assert_eq!(v.raw, b"Hello");
}

#[cfg(feature = "base64")]
#[test]
fn all_three_roundtrip() {
    let original = WithAllThree {
        id: "roundtrip".into(),
        raw: vec![1, 2, 3],
    };
    let j = json::to_string(&original);
    let parsed: WithAllThree = json::from_str(&j).unwrap();
    assert_eq!(original, parsed);
}

#[cfg(feature = "base64")]
#[test]
fn with_default_invalid_payload_type() {
    let j = r#"{"name": "test", "payload": 123}"#;
    let result: Result<WithBase64AndDefault, _> = json::from_str(j);
    assert!(result.is_err());
}