use nextjson::{from_str, to_string, NsonDeserialize, NsonSerialize};
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
struct Multi<T, U = T> {
t: T,
u: U,
}
#[test]
fn multi_type_params_with_default() {
let v = Multi { t: 1_i32, u: 2_i32 };
assert_eq!(to_string(&v).unwrap(), r#"{"t":1,"u":2}"#);
let back: Multi<i32> = from_str(r#"{"t":1,"u":2}"#).unwrap();
assert_eq!(back, v);
let v2 = Multi {
t: 1_i32,
u: "x".to_string(),
};
assert_eq!(to_string(&v2).unwrap(), r#"{"t":1,"u":"x"}"#);
let back2: Multi<i32, String> = from_str(r#"{"t":1,"u":"x"}"#).unwrap();
assert_eq!(back2, v2);
}
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
struct Blob<const N: usize> {
data: [u8; N],
tag: String,
}
#[test]
fn const_generic_array_field() {
let v = Blob::<3> {
data: [1, 2, 3],
tag: "abc".into(),
};
assert_eq!(to_string(&v).unwrap(), r#"{"data":[1,2,3],"tag":"abc"}"#);
let back: Blob<3> = from_str(r#"{"tag":"abc","data":[1,2,3]}"#).unwrap();
assert_eq!(back, v);
}
#[derive(NsonSerialize, NsonDeserialize)]
struct Marker<T> {
_m: core::marker::PhantomData<T>,
n: i32,
}
#[derive(NsonSerialize, NsonDeserialize)]
struct MarkerPath<T> {
_m: std::marker::PhantomData<T>,
n: i32,
}
#[test]
fn phantom_data_field() {
let v = Marker::<String> {
_m: core::marker::PhantomData,
n: 7,
};
assert_eq!(to_string(&v).unwrap(), r#"{"n":7}"#);
let back: Marker<String> = from_str(r#"{"n":7}"#).unwrap();
assert_eq!(back.n, 7);
let vp = MarkerPath::<String> {
_m: std::marker::PhantomData,
n: 8,
};
assert_eq!(to_string(&vp).unwrap(), r#"{"n":8}"#);
let backp: MarkerPath<String> = from_str(r#"{"n":8}"#).unwrap();
assert_eq!(backp.n, 8);
}
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
struct Nested<T> {
map: std::collections::BTreeMap<String, Vec<T>>,
opt: Option<Box<T>>,
}
#[test]
fn nested_generic_containers() {
let v = Nested {
map: std::collections::BTreeMap::from([("k".to_string(), vec![1_i32, 2])]),
opt: Some(Box::new(3)),
};
assert_eq!(to_string(&v).unwrap(), r#"{"map":{"k":[1,2]},"opt":3}"#);
let back: Nested<i32> = from_str(r#"{"opt":3,"map":{"k":[1,2]}}"#).unwrap();
assert_eq!(back, v);
}
#[derive(NsonSerialize, NsonDeserialize)]
#[njson(bound(serialize = "", deserialize = ""))]
struct IterWrap<T>
where
T: Iterator<Item = i32> + Clone,
{
items: Vec<i32>,
_p: core::marker::PhantomData<T>,
}
#[test]
fn complex_where_predicate() {
let v = IterWrap::<std::vec::IntoIter<i32>> {
items: vec![1, 2],
_p: core::marker::PhantomData,
};
assert_eq!(to_string(&v).unwrap(), r#"{"items":[1,2]}"#);
let back: IterWrap<std::vec::IntoIter<i32>> = from_str(r#"{"items":[1,2]}"#).unwrap();
assert_eq!(back.items, vec![1, 2]);
}
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
#[njson(bound(
serialize = "T: nextjson::NsonSerialize",
deserialize = "T: nextjson::NsonDeserialize<'de>"
))]
struct Bounded<T>
where
T: core::fmt::Debug,
{
value: T,
}
#[test]
fn custom_bound_keeps_where_clause() {
let v = Bounded { value: 5_i32 };
assert_eq!(to_string(&v).unwrap(), r#"{"value":5}"#);
let back: Bounded<i32> = from_str(r#"{"value":5}"#).unwrap();
assert_eq!(back, v);
}
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
enum GenericEnum<T> {
Unit,
One(T),
Many(Vec<T>),
Named { a: T, b: Option<T> },
}
#[test]
fn generic_enum_all_shapes() {
let cases: Vec<(GenericEnum<i32>, &str)> = vec![
(GenericEnum::Unit, r#"{"Unit":null}"#),
(GenericEnum::One(1), r#"{"One":1}"#),
(GenericEnum::Many(vec![1, 2]), r#"{"Many":[1,2]}"#),
(
GenericEnum::Named { a: 1, b: Some(2) },
r#"{"Named":{"a":1,"b":2}}"#,
),
];
for (value, expected) in cases {
assert_eq!(to_string(&value).unwrap(), expected);
let back: GenericEnum<i32> = from_str(expected).unwrap();
assert_eq!(back, value);
}
}
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
struct Pair<T, U>(T, U);
#[test]
fn generic_tuple_struct() {
let v = Pair(1_i32, "x".to_string());
assert_eq!(to_string(&v).unwrap(), r#"[1,"x"]"#);
let back: Pair<i32, String> = from_str(r#"[1,"x"]"#).unwrap();
assert_eq!(back, v);
}
#[derive(NsonSerialize, NsonDeserialize)]
struct Unsized<T: ?Sized> {
b: Box<T>,
}
#[test]
fn unsized_generic_param() {
let v = Unsized {
b: Box::new("hi".to_string()),
};
assert_eq!(to_string(&v).unwrap(), r#"{"b":"hi"}"#);
let back: Unsized<String> = from_str(r#"{"b":"hi"}"#).unwrap();
assert_eq!(back.b, Box::new("hi".to_string()));
}
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
struct Borrowed<'a> {
#[njson(borrow)]
name: &'a str,
id: u32,
}
#[test]
fn borrowed_lifetime_field() {
let v = Borrowed {
name: "alice",
id: 1,
};
assert_eq!(to_string(&v).unwrap(), r#"{"name":"alice","id":1}"#);
let json = r#"{"name":"bob","id":2}"#;
let back: Borrowed<'_> = from_str(json).unwrap();
assert_eq!(back, Borrowed { name: "bob", id: 2 });
}
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
#[njson(default)]
struct AllDefault<T> {
a: T,
b: i32,
}
#[test]
fn container_level_default_generic() {
let v = AllDefault { a: 1_i32, b: 2 };
assert_eq!(to_string(&v).unwrap(), r#"{"a":1,"b":2}"#);
let back: AllDefault<i32> = from_str(r#"{"a":5}"#).unwrap();
assert_eq!(back, AllDefault { a: 5, b: 0 });
}
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
struct PathOption<T> {
name: String,
note: core::option::Option<T>,
}
#[test]
fn path_spelled_option_is_optional() {
let v = PathOption {
name: "x".into(),
note: core::option::Option::Some(1_i32),
};
assert_eq!(to_string(&v).unwrap(), r#"{"name":"x","note":1}"#);
let back: PathOption<i32> = from_str(r#"{"name":"x"}"#).unwrap();
assert_eq!(back.note, core::option::Option::None);
}