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}