native_json/
lib.rs

1//!# Native JSON for Rust
2//!
3//!This crate provides native JSON syntax for Rust, it brings with a powerful way of parsing JSON syntax into native Rust structs. You can declare the JSON object natively as you do with JavaScript, JSON in Rust was made easy!
4//!
5//!## Usage
6//!Add dependencies to your `Cargo.toml`.
7//!```toml
8//![dependencies]
9//!native-json = "1.2"
10//!serde = {version = "1.0", features = ["derive"] }
11//!serde_json = "1.0"
12//!```
13//!
14//!## Example of using native JSON object
15//!```rust
16//!use native_json::json;
17//!use std::collections::HashMap;
18//!use serde::{Deserialize, Serialize};
19//!
20//!fn main()
21//!{
22//!    let mut json = json!{
23//!        name: "native json",
24//!        style: {
25//!            color: "red",
26//!            size: 12,
27//!            bold: true,
28//!            range: null
29//!        },
30//!        array: [5,4,3,2,1],
31//!        vector: vec![1,2,3,4,5],
32//!        hashmap: HashMap::from([ ("a", 1), ("b", 2), ("c", 3) ]);,
33//!        students: [
34//!            {name: "John", age: 18},
35//!            {name: "Jack", age: 21},
36//!        ],
37//!    };
38//!
39//!    // Native access
40//!    json.style.size += 1;
41//!    json.students[0].age += 2;
42//!
43//!    // Debug
44//!    println!("{:#?}", t);
45//!
46//!    // Stringify
47//!    let text = json.stringify(4);
48//!    println!("{}", text);
49//!}
50//!```
51//!## Declare a named JSON struct
52//!
53//!With JSON declare syntax, you can declare nested native JSON object in place.
54//!
55//!### JSON Declare Syntax
56//!```rust
57//!json!{
58//!JSON_OBJECT_NAME {
59//!    state: i32?,    // optional field
60//!    type_: String,  // suffix underscore will be removed when serialize & deserialize
61//!    name : type,
62//!    array: [type],
63//!    object: {
64//!        name: type,
65//!        ...
66//!    },
67//!    ...
68//!}}
69//!```
70//!
71//!The native-json will generate native Rust structs for you, each object is named by object hierarchy path, concatenated with underscore.
72//!
73//!  1. `JSON_OBJECT_NAME.object` was converted to `JSON_OBJECT_NAME_object`
74//!  2. `JSON_OBJECT_NAME.array's item` was converted to `JSON_OBJECT_NAME_array_item`
75//!
76//!## Example of using named JSON object
77//!
78//!```rust
79//!use native_json::json;
80//!use serde::{Deserialize, Serialize};
81//!use std::collections::HashMap;
82//!
83//!json!{ School {
84//!    name: String,
85//!    students: [
86//!        { name: String, age: u16 },
87//!        ...
88//!    ],
89//!    map: HashMap<String, String>,
90//!    nullable: Option<String>
91//!}}
92//!
93//!fn main()
94//!{
95//!    let mut school = School::new();
96//!
97//!    school.name = "MIT".to_string();
98//!    school.map.insert("Tom".to_owned(), "Profile".to_owned());
99//!
100//!    // using initializer
101//!    let mut john = School_students_item::new();
102//!    john.name = "John".to_owned();
103//!    john.age = 18;
104//!    school.students.push(john);
105//!
106//!    // using struct
107//!    let jack = School_students_item { name: "Jack".to_string(), age: 21 };
108//!    school.students.push(jack);
109//!
110//!    // show
111//!    println!("{:#?}", school);
112//!}
113//!```
114//!
115use std::fs::File;
116use std::fs::OpenOptions;
117use std::io::BufReader;
118use std::io::BufWriter;
119use std::path::Path;
120
121pub use native_json_macro::*;
122pub use serde::de::DeserializeOwned;
123pub use serde::{Deserialize, Serialize};
124pub use serde_json::from_str as parse;
125pub use serde_json::Error;
126
127// #[serde(default, skip_serializing_if = "is_default")]
128pub fn is_default<T: Default + PartialEq>(t: &T) -> bool {
129    t == &T::default()
130}
131
132pub trait JSON: Serialize {
133    /// Return a concise JSON string
134    fn string(&self) -> anyhow::Result<String> {
135        self.stringify(0)
136    }
137
138    /// Stringify a native-json object
139    ///
140    /// indent
141    ///
142    /// - 0 : output concise JSON string
143    /// - N : pretty output with N spaces indentation
144    fn stringify(&self, indent: usize) -> anyhow::Result<String> {
145        // concise
146        if indent == 0 {
147            return Ok(serde_json::to_string(self)?);
148        }
149
150        // pretty
151        let buf = Vec::new();
152        let spaces = vec![' ' as u8; indent];
153        let formatter = serde_json::ser::PrettyFormatter::with_indent(&spaces);
154        let mut ser = serde_json::Serializer::with_formatter(buf, formatter);
155        self.serialize(&mut ser)?;
156        let output = String::from_utf8(ser.into_inner())?;
157
158        Ok(output)
159    }
160}
161
162impl<T> JSON for T where T: Serialize {}
163
164/// Deserialize from file
165pub fn read<T, P: AsRef<Path>>(path: P) -> anyhow::Result<T>
166where
167    T: DeserializeOwned,
168{
169    let file = File::open(path)?;
170    let reader = BufReader::new(file);
171    let value = serde_json::from_reader(reader)?;
172    Ok(value)
173}
174
175/// Serialize into file
176pub fn write<T, P: AsRef<Path>>(path: P, value: &T) -> anyhow::Result<()>
177where
178    T: Serialize,
179{
180    let file = OpenOptions::new()
181        .write(true)
182        .create(true)
183        .truncate(true)
184        .open(path)?;
185    let writer = BufWriter::new(file);
186    Ok(serde_json::to_writer_pretty(writer, value)?)
187}
188
189pub struct Writer<'a> {
190    path: &'a Path,
191    indent: usize,
192}
193
194impl<'a> Writer<'a> {
195    /// Indentation
196    pub fn indent(mut self, n: usize) -> Self {
197        self.indent = n;
198        self
199    }
200
201    /// Write the value into file
202    pub fn write<T>(&self, value: &T) -> anyhow::Result<()>
203    where
204        T: Serialize,
205    {
206        let spaces = vec![' ' as u8; self.indent];
207        let file = OpenOptions::new()
208            .write(true)
209            .create(true)
210            .truncate(true)
211            .open(self.path)?;
212        let writer = BufWriter::new(file);
213        let formatter = serde_json::ser::PrettyFormatter::with_indent(&spaces);
214        let mut ser = serde_json::Serializer::with_formatter(writer, formatter);
215        Ok(value.serialize(&mut ser)?)
216    }
217}
218
219/// Build a file writer
220pub fn writer<'a, P>(path: &'a P) -> Writer<'a>
221where
222    P: AsRef<Path>,
223{
224    Writer {
225        path: path.as_ref(),
226        indent: 2,
227    }
228}