Skip to main content

derive/
derive.rs

1//! Derive-macro workflow — `Serialize` and `Deserialize`.
2//!
3//! Annotate your structs and enums and get serialize/deserialize for free.
4//! Rename fields with `#[nanojson(rename = "...")]`.
5//!
6//! Shows both API tiers:
7//!
8//! **`std` tier**: `nanojson::stringify` / `nanojson::parse` — one-liners,
9//! no buffer choices.
10//!
11//! **`no_std` tier**: `nanojson::stringify_sized::<N, _>` / `nanojson::parse_sized::<STR_BUF, _>` —
12//! all memory on the stack, caller picks sizes.
13
14extern crate std;
15
16use nanojson::{Serialize, Deserialize};
17
18// ---- Domain types ----
19
20#[derive(Serialize, Deserialize, Debug, PartialEq)]
21struct Vec2 {
22    x: i64,
23    y: i64,
24}
25
26#[derive(Serialize, Deserialize, Debug, PartialEq)]
27struct Entity {
28    id: i64,
29    #[nanojson(rename = "is_active")]
30    active: bool,
31    position: Vec2,
32    health: i64,
33}
34
35// Unit enums serialize to/from JSON strings.
36#[derive(Serialize, Deserialize, Debug, PartialEq)]
37enum Team {
38    Red,
39    Blue,
40    #[nanojson(rename = "spectator")]
41    Spectator,
42}
43
44// Enums with data use externally-tagged format: {"VariantName": {...}}.
45#[derive(Serialize, Deserialize, Debug, PartialEq)]
46enum Event {
47    Spawn { entity_id: i64, x: i64, y: i64 },
48    Death { entity_id: i64 },
49}
50
51fn main() {
52    let entity = Entity {
53        id: 42,
54        active: true,
55        position: Vec2 { x: 100, y: -50 },
56        health: 80,
57    };
58
59    // ----------------------------------------------------------------
60    // std tier — String in, String out, no buffer choices
61    // ----------------------------------------------------------------
62
63    let json = nanojson::stringify(&entity).unwrap();
64    std::println!("Entity JSON (std): {json}");
65
66    let entity2: Entity = nanojson::parse(&json).unwrap();
67    std::println!("Decoded (std):     {:?}", entity2);
68    assert_eq!(entity, entity2);
69
70    // ----------------------------------------------------------------
71    // no_std tier — stack-allocated buffers, explicit sizes
72    //
73    // stringify_sized::<N, _>  — N bytes for the output JSON.
74    // parse_sized::<S, _> — S bytes for the string-decode scratch buffer;
75    //                     only needs to fit the longest single field value.
76    // ----------------------------------------------------------------
77
78    let mut buf = [0; 256];
79    let json = nanojson::stringify_sized(&mut buf, &entity).unwrap();
80    std::println!("\nEntity JSON (no_std, {} bytes): {}", json.len(), json);
81
82    let entity3: Entity = nanojson::parse_sized(&mut [0; 64], json).unwrap();
83    std::println!("Decoded (no_std):  {:?}", entity3);
84    assert_eq!(entity, entity3);
85
86    // ----------------------------------------------------------------
87    // Unit enum — std and no_std
88    // ----------------------------------------------------------------
89
90    let team = Team::Spectator;
91
92    let json = nanojson::stringify(&team).unwrap();
93    std::println!("\nTeam JSON (std):    {json}");
94    let team2: Team = nanojson::parse(&json).unwrap();
95    assert_eq!(team, team2);
96
97    let mut buf = [0; 16];
98    let json = nanojson::stringify_sized(&mut buf, &team).unwrap();
99    std::println!("Team JSON (no_std): {}", json);
100    let team3: Team = nanojson::parse_sized(&mut [0; 16], json).unwrap();
101    assert_eq!(team, team3);
102
103    // ----------------------------------------------------------------
104    // Data enum
105    // ----------------------------------------------------------------
106
107    let events: [Event; 2] = [
108        Event::Spawn { entity_id: 1, x: 0, y: 0 },
109        Event::Death { entity_id: 1 },
110    ];
111
112    for ev in &events {
113        // std
114        let json = nanojson::stringify(ev).unwrap();
115        std::println!("\nEvent JSON (std):    {json}");
116        let ev2: Event = nanojson::parse(&json).unwrap();
117        assert_eq!(*ev, ev2);
118
119        // no_std
120        let mut buf = [0; 128];
121        let json = nanojson::stringify_sized(&mut buf, ev).unwrap();
122        std::println!("Event JSON (no_std): {}", json);
123        let ev3: Event = nanojson::parse_sized(&mut [0; 32], json).unwrap();
124        assert_eq!(*ev, ev3);
125    }
126
127    // ----------------------------------------------------------------
128    // Error handling — unknown field, missing field
129    // ----------------------------------------------------------------
130
131    let bad = r#"{"id":1,"is_active":true,"position":{"x":0,"y":0},"health":100,"unknown":999}"#;
132    match nanojson::parse::<Entity>(bad) {
133        Err(e) => std::println!("\nExpected error (unknown field): {:?} at offset {}", e.kind, e.offset),
134        Ok(_)  => panic!("should have failed"),
135    }
136
137    let incomplete = r#"{"id":2,"is_active":false,"position":{"x":1,"y":2}}"#;
138    match nanojson::parse::<Entity>(incomplete) {
139        Err(e) => std::println!("Expected error (missing field): {:?} at offset {}", e.kind, e.offset),
140        Ok(_)  => panic!("should have failed"),
141    }
142
143    // ----------------------------------------------------------------
144    // Size estimation — measure before picking N
145    // ----------------------------------------------------------------
146
147    let n = nanojson::measure(|s| entity.serialize(s));
148    std::println!("\nEntity serializes to {n} bytes — use stringify_sized::<{n}, _> or larger.");
149}
150
151#[cfg(test)] #[test] fn test_main() { main() }