sppparse/lib.rs
1//! Provides a high level way of lazily dereferencing JSON Pointer in [serde](serde) [`Value`](serde_json::Value).
2//!
3//! It can operate in-memory or on files (`JSON` or `YAML`).
4//!
5//! To deserialize an object of type `T` or a pointer to
6//! local or distant document referencing an object of type `T`,
7//! we use the type [SparseSelector](crate::SparseSelector).
8//!
9//! The root document is wrapped in a [SparseRoot](crate::SparseRoot).
10//! This allow a coordination of the state and the cached values.
11//!
12//! Let's take the following `JSON` document :
13//! ```json
14//! {
15//! "hello": "world",
16//! "obj": {
17//! "key1": {
18//! "$ref": "#/hello"
19//! },
20//! "key2": "universe"
21//! }
22//! }
23//! ```
24//!
25//! Now, let's parse it using the [SparseSelector](crate::SparseSelector) and the [SparseRoot](crate::SparseRoot) :
26//!
27//! ```rust
28//! extern crate sppparse;
29//!
30//! use serde::{Deserialize, Serialize};
31//! use sppparse::{Sparsable, SparsePointer, SparseRoot, SparseSelector};
32//! use std::collections::HashMap;
33//! use std::path::PathBuf;
34//!
35//! #[derive(Debug, Deserialize, Serialize, Sparsable)]
36//! struct ObjectExampleParsed {
37//! hello: String,
38//! obj: HashMap<String, SparseSelector<String>>,
39//! }
40//!
41//! fn main() {
42//! let doc: SparseRoot<ObjectExampleParsed> = SparseRoot::new_from_file(PathBuf::from(concat!(
43//! env!("CARGO_MANIFEST_DIR"),
44//! "/",
45//! "./examples/read_multi_files.json"
46//! )))
47//! .unwrap();
48//! println!("Full object {:#?}", doc.root_get().unwrap());
49//! println!(
50//! "A single ref {:#?}",
51//! doc.root_get().unwrap().obj.get("key1").unwrap().get()
52//! );
53//! println!(
54//! "A single ref {:#?}",
55//! doc.root_get().unwrap().obj.get("key2").unwrap().get()
56//! );
57//! }
58//! ```
59//! ## In-memory
60//!
61//! Let's take the following `JSON` example document:
62//!
63//! ```json
64//! {
65//! "hello": "world",
66//! "obj": {
67//! "key1":
68//! {
69//! "$ref": "#/hello"
70//! }
71//! }
72//! }
73//! ```
74//!
75//! We can just pass [Value](serde_json::Value) or objects that implements [Serialize](serde::Serialize) to the [SparseRoot](crate::SparseRoot)
76//!
77//! ```rust
78//! extern crate sppparse;
79//!
80//! use serde::{Deserialize, Serialize};
81//! use serde_json::json;
82//! use sppparse::{Sparsable, SparsePointer, SparseRoot, SparseSelector};
83//! use std::collections::HashMap;
84//! use std::path::PathBuf;
85//!
86//! #[derive(Debug, Deserialize, Serialize, Sparsable)]
87//! struct ObjectExampleParsed {
88//! hello: String,
89//! obj: HashMap<String, SparseSelector<String>>,
90//! }
91//!
92//! fn main() {
93//! let json_value = json!({
94//! "hello": "world",
95//! "obj": {
96//! "key1": {
97//! "$ref": "#/hello"
98//! }
99//! }
100//! });
101//! let parsed_obj: SparseRoot<ObjectExampleParsed> =
102//! SparseRoot::new_from_value(json_value, PathBuf::from("hello.json"), vec![]).unwrap();
103//!
104//! println!(
105//! "{}",
106//! parsed_obj
107//! .root_get()
108//! .unwrap()
109//! .obj
110//! .get("key1")
111//! .unwrap()
112//! .get()
113//! .expect("the dereferenced pointer")
114//! );
115//! }
116//! ```
117//! ## File backed
118//!
119//! If we take the same object as the in-memory example, but reading from a file,
120//! the rust code would like the following :
121//!
122//! ```rust
123//! extern crate sppparse;
124//!
125//! use serde::{Deserialize, Serialize};
126//! use sppparse::{Sparsable, SparsePointer, SparseRoot, SparseSelector};
127//! use std::collections::HashMap;
128//! use std::path::PathBuf;
129//!
130//! #[derive(Debug, Deserialize, Serialize, Sparsable)]
131//! struct ObjectExampleParsed {
132//! hello: String,
133//! obj: HashMap<String, SparseSelector<String>>,
134//! }
135//!
136//! fn main() {
137//! let val: SparseRoot<ObjectExampleParsed> = SparseRoot::new_from_file(PathBuf::from(concat!(
138//! env!("CARGO_MANIFEST_DIR"),
139//! "/",
140//! "./examples/read_single_file.json"
141//! )))
142//! .unwrap();
143//!
144//! println!(
145//! "{}",
146//! val.root_get()
147//! .unwrap()
148//! .obj
149//! .get("key1")
150//! .unwrap()
151//! .get()
152//! .expect("the dereferenced pointer")
153//! );
154//! }
155//! ```
156//!
157//! ## Updates
158//!
159//! Using [Sparse](crate), it's also possible to modify the parsed value and then save them to disk.
160//!
161//! See the following example :
162//!
163//! ```rust
164//! extern crate sppparse;
165//! use serde::{Deserialize, Serialize};
166//! use sppparse::{Sparsable, SparsePointer, SparseRoot, SparseSelector};
167//! use std::collections::HashMap;
168//! use std::path::PathBuf;
169//!
170//! #[derive(Debug, Deserialize, Serialize, Sparsable)]
171//! struct ObjectExampleParsed {
172//! hello: String,
173//! obj: HashMap<String, SparseSelector<String>>,
174//! }
175//!
176//! fn main() {
177//! let mut val: SparseRoot<ObjectExampleParsed> = SparseRoot::new_from_file(PathBuf::from(concat!(
178//! env!("CARGO_MANIFEST_DIR"),
179//! "/",
180//! "./examples/read_single_file.json"
181//! )))
182//! .unwrap();
183//!
184//! println!(
185//! "Before : {}",
186//! val.root_get()
187//! .unwrap()
188//! .obj
189//! .get("key1")
190//! .unwrap()
191//! .get()
192//! .expect("the dereferenced pointer")
193//! );
194//! {
195//! let state = val.state().clone();
196//! let mut root_mut = val.root_get_mut().unwrap();
197//!
198//! let key1 = root_mut.obj.get_mut("key1").unwrap();
199//! let mut key1_deref = key1.get_mut(state).unwrap();
200//!
201//! *key1_deref = "universe".to_string();
202//! key1_deref.sparse_save().unwrap();
203//! val.sparse_updt().unwrap();
204//! }
205//! println!(
206//! "After : {}",
207//! val.root_get()
208//! .unwrap()
209//! .obj
210//! .get("key1")
211//! .unwrap()
212//! .get()
213//! .expect("the dereferenced pointer")
214//! );
215//! // To persist those modification to disk use :
216//! //
217//! // val.save_to_disk(None).unwrap()
218//! }
219//! ```
220#![warn(clippy::all)]
221
222mod sparsable;
223mod sparse_errors;
224mod sparse_metadata;
225mod sparse_pointed_value;
226mod sparse_pointer;
227mod sparse_ref;
228mod sparse_ref_raw;
229mod sparse_ref_raw_inline;
230mod sparse_root;
231mod sparse_selector;
232mod sparse_state;
233mod sparse_value;
234mod sparse_value_mut;
235
236/// The max stack frames [Sparse](crate) will go before returning a [cyclic](crate::SparseError::CyclicRef).
237///
238/// For each [SparseSelector](crate::SparseSelector) in your objects, you should count 3 stack frames.
239///
240/// i.e. If you have a document with a depth of 30 references. The maximum depth of the recursive function will be
241/// at most 90.
242pub const MAX_SPARSE_DEPTH: u32 = 100;
243
244#[cfg(test)]
245pub(crate) mod tests;
246
247pub use crate::sparse_errors::SparseError;
248pub use crate::sparse_state::{SparseFileFormat, SparseState, SparseStateFile};
249use getset::{CopyGetters, Getters, MutGetters};
250use serde::{de::DeserializeOwned, Deserialize, Serialize};
251use serde_json::Value;
252pub use sparsable::Sparsable as SparsableTrait;
253pub use sparse_metadata::SparseMetadata;
254pub use sparse_pointed_value::SparsePointedValue;
255pub use sparse_pointer::{SparsePointer, SparsePointerRaw};
256pub use sparse_ref::SparseRef;
257pub use sparse_ref_raw::SparseRefRaw;
258pub use sparse_ref_raw_inline::SparseRefRawInline;
259pub use sparse_root::SparseRoot;
260pub use sparse_selector::SparseSelector;
261pub use sparse_value::SparseValue;
262pub use sparse_value_mut::SparseValueMut;
263pub use sppparse_derive::Sparsable;
264
265use std::cell::RefCell;
266use std::collections::HashMap;
267use std::convert::From;
268use std::path::PathBuf;
269use std::rc::Rc;