use nextjson::{from_str, to_string, NsonDeserialize, NsonSerialize, Number, Value};
#[test]
fn external_unit_variants() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
enum Color {
Red,
Green,
Blue,
}
assert_eq!(to_string(&Color::Red).unwrap(), r#"{"Red":null}"#);
let back: Color = from_str(r#"{"Green":null}"#).unwrap();
assert_eq!(back, Color::Green);
assert!(from_str::<Color>(r#"{"Purple":null}"#).is_err());
}
#[test]
fn external_newtype_variant() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
enum Shape {
Circle(f64),
Rect { w: f64, h: f64 },
}
assert_eq!(to_string(&Shape::Circle(1.5)).unwrap(), r#"{"Circle":1.5}"#);
let back: Shape = from_str(r#"{"Circle":2.0}"#).unwrap();
assert_eq!(back, Shape::Circle(2.0));
let r = Shape::Rect { w: 3.0, h: 4.0 };
assert_eq!(to_string(&r).unwrap(), r#"{"Rect":{"w":3.0,"h":4.0}}"#);
let back: Shape = from_str(r#"{"Rect":{"h":4.0,"w":3.0}}"#).unwrap();
assert_eq!(back, r);
}
#[test]
fn external_tuple_variant() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
enum Event {
Point(i32, i32),
}
assert_eq!(
to_string(&Event::Point(1, 2)).unwrap(),
r#"{"Point":[1,2]}"#
);
let back: Event = from_str(r#"{"Point":[1,2]}"#).unwrap();
assert_eq!(back, Event::Point(1, 2));
assert!(from_str::<Event>(r#"{"Point":[1]}"#).is_err());
}
#[test]
fn rename_all_variants() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
#[njson(rename_all = "snake_case")]
enum Status {
InProgress,
Done,
}
assert_eq!(
to_string(&Status::InProgress).unwrap(),
r#"{"in_progress":null}"#
);
let back: Status = from_str(r#"{"done":null}"#).unwrap();
assert_eq!(back, Status::Done);
}
#[test]
fn variant_rename() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
enum E {
#[njson(rename = "first")]
First,
}
assert_eq!(to_string(&E::First).unwrap(), r#"{"first":null}"#);
let back: E = from_str(r#"{"first":null}"#).unwrap();
assert_eq!(back, E::First);
}
#[test]
fn internally_tagged() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
#[njson(tag = "kind")]
enum Msg {
Hello { name: String },
Bye,
}
let m = Msg::Hello {
name: "alice".into(),
};
assert_eq!(to_string(&m).unwrap(), r#"{"kind":"Hello","name":"alice"}"#);
let back: Msg = from_str(r#"{"name":"bob","kind":"Hello"}"#).unwrap();
assert_eq!(back, Msg::Hello { name: "bob".into() });
assert_eq!(to_string(&Msg::Bye).unwrap(), r#"{"kind":"Bye"}"#);
let back: Msg = from_str(r#"{"kind":"Bye"}"#).unwrap();
assert_eq!(back, Msg::Bye);
assert!(from_str::<Msg>(r#"{"name":"x"}"#).is_err());
assert!(from_str::<Msg>(r#"{"kind":"Nope"}"#).is_err());
}
#[test]
fn internally_tagged_newtype_map() {
use std::collections::BTreeMap;
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
#[njson(tag = "type")]
enum Cmd {
Set(BTreeMap<String, i32>),
}
let c = Cmd::Set(BTreeMap::from([("k".to_string(), 1)]));
assert_eq!(to_string(&c).unwrap(), r#"{"type":"Set","k":1}"#);
let back: Cmd = from_str(r#"{"type":"Set","k":2}"#).unwrap();
assert_eq!(back, Cmd::Set(BTreeMap::from([("k".to_string(), 2)])));
}
#[test]
fn adjacently_tagged() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
#[njson(tag = "t", content = "c")]
enum Op {
Add(i32, i32),
Named { id: String },
Clear,
}
let a = Op::Add(1, 2);
assert_eq!(to_string(&a).unwrap(), r#"{"t":"Add","c":[1,2]}"#);
let back: Op = from_str(r#"{"t":"Add","c":[3,4]}"#).unwrap();
assert_eq!(back, Op::Add(3, 4));
let n = Op::Named { id: "x".into() };
assert_eq!(to_string(&n).unwrap(), r#"{"t":"Named","c":{"id":"x"}}"#);
let back: Op = from_str(r#"{"t":"Named","c":{"id":"y"}}"#).unwrap();
assert_eq!(back, Op::Named { id: "y".into() });
assert_eq!(to_string(&Op::Clear).unwrap(), r#"{"t":"Clear"}"#);
let back: Op = from_str(r#"{"t":"Clear"}"#).unwrap();
assert_eq!(back, Op::Clear);
assert!(from_str::<Op>(r#"{"t":"Clear","c":1}"#).is_err());
assert!(from_str::<Op>(r#"{"t":"Add","c":[1,2],"extra":1}"#).is_err());
}
#[test]
fn untagged() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
#[njson(untagged)]
enum Value {
Num(f64),
Text(String),
Flag(bool),
List(Vec<i32>),
Obj { x: i32 },
}
let v = Value::Num(1.5);
assert_eq!(to_string(&v).unwrap(), "1.5");
assert_eq!(from_str::<Value>("2.5").unwrap(), Value::Num(2.5));
assert_eq!(to_string(&Value::Text("hi".into())).unwrap(), r#""hi""#);
assert_eq!(
from_str::<Value>(r#""yo""#).unwrap(),
Value::Text("yo".into())
);
assert_eq!(to_string(&Value::Flag(true)).unwrap(), "true");
assert_eq!(from_str::<Value>("false").unwrap(), Value::Flag(false));
assert_eq!(to_string(&Value::List(vec![1, 2])).unwrap(), "[1,2]");
assert_eq!(from_str::<Value>("[3]").unwrap(), Value::List(vec![3]));
let o = Value::Obj { x: 7 };
assert_eq!(to_string(&o).unwrap(), r#"{"x":7}"#);
assert_eq!(
from_str::<Value>(r#"{"x":9}"#).unwrap(),
Value::Obj { x: 9 }
);
assert!(from_str::<Value>("null").is_err());
}
#[test]
fn untagged_fallback_order() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
#[njson(untagged)]
enum Either {
Int(i64),
Float(f64),
}
assert_eq!(from_str::<Either>("42").unwrap(), Either::Int(42));
assert_eq!(from_str::<Either>("4.5").unwrap(), Either::Float(4.5));
}
#[test]
fn generic_enum() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
enum Tree<T> {
Leaf(T),
Node {
left: Box<Tree<T>>,
right: Box<Tree<T>>,
},
}
let t = Tree::Node {
left: Box::new(Tree::Leaf(1)),
right: Box::new(Tree::Leaf(2)),
};
let text = to_string(&t).unwrap();
assert_eq!(text, r#"{"Node":{"left":{"Leaf":1},"right":{"Leaf":2}}}"#);
let back: Tree<i32> = from_str(&text).unwrap();
assert_eq!(back, t);
}
#[test]
fn enum_with_defaults_and_skips() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
#[njson(tag = "kind")]
enum W {
A {
x: i32,
#[njson(default)]
y: i32,
#[njson(skip_serializing, default)]
secret: String,
},
}
let w = W::A {
x: 1,
y: 2,
secret: "s".into(),
};
assert_eq!(to_string(&w).unwrap(), r#"{"kind":"A","x":1,"y":2}"#);
let back: W = from_str(r#"{"kind":"A","x":5}"#).unwrap();
match back {
W::A { x, y, secret } => {
assert_eq!(x, 5);
assert_eq!(y, 0);
assert_eq!(secret, "");
}
}
}
#[test]
fn schema_of_works() {
#[derive(NsonSerialize)]
#[njson(rename_all = "camelCase")]
struct Person {
first_name: String,
age: u32,
tags: Vec<String>,
active: bool,
}
let s = nextjson::schema_of::<Person>();
assert_eq!(s.name(), "Person");
let json_schema = nextjson::to_json_schema::<Person>();
assert_eq!(json_schema["type"], "object".into());
assert_eq!(
json_schema["properties"]["firstName"]["type"],
"string".into()
);
assert_eq!(json_schema["properties"]["age"]["type"], "integer".into());
assert_eq!(json_schema["properties"]["tags"]["type"], "array".into());
assert_eq!(
json_schema["properties"]["active"]["type"],
"boolean".into()
);
}
#[test]
fn to_value_from_value() {
#[derive(NsonSerialize, NsonDeserialize, Debug, PartialEq)]
struct S {
a: i32,
b: Vec<String>,
}
let s = S {
a: 1,
b: vec!["x".into(), "y".into()],
};
let v = nextjson::to_value(&s).unwrap();
assert_eq!(v["a"], Value::Number(Number::U64(1)));
assert_eq!(v["b"][1], "y".into());
let back: S = nextjson::from_value(v).unwrap();
assert_eq!(back, s);
}