use midiserde::{from_value, json, to_value, Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Deserialize, Serialize)]
struct UserWithExtra {
id: String,
username: String,
#[mini(flatten)]
extra: HashMap<String, miniserde::json::Value>,
}
#[test]
fn flatten_capture_unknown_fields() {
let json_str = r#"{"id": "123", "username": "alice", "mascot": "Ferris", "score": 42}"#;
let value: miniserde::json::Value = json::from_str(json_str).unwrap();
let v: UserWithExtra = from_value(&value).unwrap();
assert_eq!(v.id, "123");
assert_eq!(v.username, "alice");
assert!(v.extra.contains_key("mascot"));
assert!(v.extra.contains_key("score"));
assert_eq!(v.extra.len(), 2);
}
#[test]
fn flatten_capture_roundtrip() {
let original = UserWithExtra {
id: "456".into(),
username: "bob".into(),
extra: [("mascot".into(), miniserde::json::Value::String("crab".into()))]
.into_iter()
.collect(),
};
let value = to_value(&original);
let restored: UserWithExtra = from_value(&value).unwrap();
assert_eq!(original.id, restored.id);
assert_eq!(original.username, restored.username);
assert_eq!(original.extra.len(), restored.extra.len());
}
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct Pagination {
limit: u64,
offset: u64,
total: u64,
}
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct UsersPaginated {
users: Vec<String>,
#[mini(flatten)]
pagination: Pagination,
}
#[test]
fn flatten_factor_out_struct() {
let json_str = r#"{"users": ["a", "b"], "limit": 100, "offset": 200, "total": 1053}"#;
let value: miniserde::json::Value = json::from_str(json_str).unwrap();
let v: UsersPaginated = from_value(&value).unwrap();
assert_eq!(v.users, vec!["a", "b"]);
assert_eq!(v.pagination.limit, 100);
assert_eq!(v.pagination.offset, 200);
assert_eq!(v.pagination.total, 1053);
}
#[derive(Debug, Deserialize, Serialize)]
struct UsersWithExtra {
users: Vec<String>,
#[mini(flatten)]
pagination: Pagination,
#[mini(flatten)]
extra: HashMap<String, miniserde::json::Value>,
}
#[test]
fn flatten_combination() {
let json_str = r#"{"users": ["a"], "limit": 10, "offset": 0, "total": 1, "mascot": "Ferris"}"#;
let value: miniserde::json::Value = json::from_str(json_str).unwrap();
let v: UsersWithExtra = from_value(&value).unwrap();
assert_eq!(v.users, vec!["a"]);
assert_eq!(v.pagination.limit, 10);
assert_eq!(v.pagination.offset, 0);
assert_eq!(v.pagination.total, 1);
assert!(v.extra.contains_key("mascot"));
}
#[test]
fn flatten_combination_roundtrip() {
let original = UsersWithExtra {
users: vec!["x".into(), "y".into()],
pagination: Pagination {
limit: 10,
offset: 0,
total: 25,
},
extra: [
("mascot".into(), miniserde::json::Value::String("Ferris".into())),
(
"score".into(),
miniserde::json::Value::Number(miniserde::json::Number::F64(42.0)),
),
]
.into_iter()
.collect(),
};
let value = to_value(&original);
let restored: UsersWithExtra = from_value(&value).unwrap();
assert_eq!(original.users, restored.users);
assert_eq!(original.pagination.limit, restored.pagination.limit);
assert_eq!(original.pagination.offset, restored.pagination.offset);
assert_eq!(original.pagination.total, restored.pagination.total);
assert_eq!(original.extra.len(), restored.extra.len());
assert!(restored.extra.contains_key("mascot"));
assert!(restored.extra.contains_key("score"));
}
#[test]
fn flatten_factor_out_roundtrip() {
let original = UsersPaginated {
users: vec!["x".into(), "y".into()],
pagination: Pagination {
limit: 10,
offset: 0,
total: 25,
},
};
let value = to_value(&original);
let restored: UsersPaginated = from_value(&value).unwrap();
assert_eq!(original.users, restored.users);
assert_eq!(original.pagination.limit, restored.pagination.limit);
assert_eq!(original.pagination.offset, restored.pagination.offset);
assert_eq!(original.pagination.total, restored.pagination.total);
}
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct Metadata {
source: String,
version: u64,
}
#[derive(Debug, Deserialize, Serialize)]
struct FullResponse {
id: String,
#[mini(flatten)]
pagination: Pagination,
#[mini(flatten)]
metadata: Metadata,
}
#[test]
fn flatten_multi_struct() {
let json_str = r#"{"id":"resp-1","limit":50,"offset":10,"total":500,"source":"api","version":3}"#;
let value: miniserde::json::Value = json::from_str(json_str).unwrap();
let v: FullResponse = from_value(&value).unwrap();
assert_eq!(v.id, "resp-1");
assert_eq!(v.pagination.limit, 50);
assert_eq!(v.pagination.offset, 10);
assert_eq!(v.pagination.total, 500);
assert_eq!(v.metadata.source, "api");
assert_eq!(v.metadata.version, 3);
}
#[test]
fn flatten_multi_struct_roundtrip() {
let original = FullResponse {
id: "resp-42".into(),
pagination: Pagination {
limit: 25,
offset: 0,
total: 100,
},
metadata: Metadata {
source: "internal".into(),
version: 7,
},
};
let value = to_value(&original);
let restored: FullResponse = from_value(&value).unwrap();
assert_eq!(original.id, restored.id);
assert_eq!(original.pagination, restored.pagination);
assert_eq!(original.metadata, restored.metadata);
}
#[derive(Debug, Deserialize, Serialize)]
struct FullResponseWithExtra {
id: String,
#[mini(flatten)]
pagination: Pagination,
#[mini(flatten)]
metadata: Metadata,
#[mini(flatten)]
extra: HashMap<String, miniserde::json::Value>,
}
#[test]
fn flatten_multi_struct_plus_map() {
let json_str = r#"{"id":"r1","limit":10,"offset":0,"total":42,"source":"api","version":1,"debug":"true"}"#;
let value: miniserde::json::Value = json::from_str(json_str).unwrap();
let v: FullResponseWithExtra = from_value(&value).unwrap();
assert_eq!(v.id, "r1");
assert_eq!(v.pagination.limit, 10);
assert_eq!(v.metadata.source, "api");
assert_eq!(v.metadata.version, 1);
assert!(v.extra.contains_key("debug"));
assert_eq!(v.extra.len(), 1);
}
#[test]
fn flatten_multi_struct_plus_map_roundtrip() {
let original = FullResponseWithExtra {
id: "r2".into(),
pagination: Pagination {
limit: 5,
offset: 10,
total: 50,
},
metadata: Metadata {
source: "webhook".into(),
version: 2,
},
extra: [("trace_id".into(), miniserde::json::Value::String("abc-123".into()))]
.into_iter()
.collect(),
};
let value = to_value(&original);
let restored: FullResponseWithExtra = from_value(&value).unwrap();
assert_eq!(original.id, restored.id);
assert_eq!(original.pagination, restored.pagination);
assert_eq!(original.metadata, restored.metadata);
assert_eq!(original.extra.len(), restored.extra.len());
assert!(restored.extra.contains_key("trace_id"));
}