jupiter_rs/infograph/
builder.rs

1//! Provides `DocBuilder` which used to create a `Doc` programmatically.
2//!
3//! Many of the performance optimizations of `Infograph` are based on the fact
4//! that a `Doc` is created once and then never modified. (Of course a new Doc can be created
5//! to replace a current version, but the data itself is immutable).
6//!
7//! Therefore a `DocBuilder` along with the helpers provided here is used to setup the data and
8//! then converted into a `Doc` as a final step.
9//!
10//! Note that there are also helpers like [yaml::hash_to_doc](crate::infograph::yaml::hash_to_doc)
11//! or [yaml::list_to_doc](crate::infograph::yaml::list_to_doc) which directly transform a given
12//! `Yaml` hash or list into a `Doc`.
13use crate::infograph::docs::Doc;
14use crate::infograph::node::Node;
15use crate::infograph::symbols::{Symbol, SymbolMap, SymbolTable};
16
17/// Provides a builder to generate a `Doc`.
18///
19/// A doc an internally have either a list or a map as its root node. Therefore either
20/// `root_object_builder()`or `root_list_builder()` has to be called the retrieve the
21/// appropriate builder after which `build()` must be called to create the resulting `Doc`.
22///
23/// # Examples
24///
25/// Creating a list based `Doc`:
26/// ```
27/// # use jupiter::infograph::builder::DocBuilder;
28/// let mut builder = DocBuilder::new();
29/// let mut list_builder = builder.root_list_builder();
30/// list_builder.append_int(1);
31/// list_builder.append_int(2);
32/// list_builder.append_int(3);
33///
34/// let doc = builder.build();
35/// assert_eq!(doc.root().at(1).as_int().unwrap(), 2);
36/// ```
37///
38/// Creating a map based `Doc`:
39/// ```
40/// # use jupiter::infograph::builder::DocBuilder;
41/// let mut builder = DocBuilder::new();
42/// let mut obj_builder = builder.root_object_builder();
43/// obj_builder.put_int("Test", 1);
44/// obj_builder.put_int("Foo", 2);
45///
46/// let doc = builder.build();
47/// assert_eq!(doc.root().query("Test").as_int().unwrap(), 1);
48/// assert_eq!(doc.root().query("Foo").as_int().unwrap(), 2);
49/// ```
50pub struct DocBuilder {
51    symbols: SymbolTable,
52    root: Node,
53}
54
55impl DocBuilder {
56    /// Creates a new builder instance.
57    pub fn new() -> Self {
58        DocBuilder {
59            symbols: SymbolTable::new(),
60            root: Node::Empty,
61        }
62    }
63
64    /// Resolves the given name into a `Symbol` for repeated insertions.
65    ///
66    /// # Errors
67    /// If the internal symbol table overflows, an error is returned.
68    pub fn resolve(&mut self, symbol: impl AsRef<str>) -> anyhow::Result<Symbol> {
69        self.symbols.find_or_create(symbol)
70    }
71
72    /// Makes the root node of the `Doc` a map and returns a builder for it.
73    ///
74    /// Note that for each `DocBuilder` either `root_object_builder` or `root_list_builder`
75    /// has to called exactly once.  
76    pub fn root_object_builder(&mut self) -> ObjectBuilder {
77        self.root = Node::Object(SymbolMap::new());
78        if let Node::Object(ref mut map) = self.root {
79            ObjectBuilder {
80                symbols: &mut self.symbols,
81                map,
82            }
83        } else {
84            unreachable!("Concurrent modification or corruption a DocBuilder")
85        }
86    }
87
88    /// Makes the root node of the `Doc` a list and returns a builder for it.
89    ///
90    /// Note that for each `DocBuilder` either `root_object_builder` or `root_list_builder`
91    /// has to called exactly once.  
92    pub fn root_list_builder(&mut self) -> ListBuilder {
93        self.root = Node::List(Vec::new());
94
95        if let Node::List(ref mut list) = self.root {
96            ListBuilder {
97                symbols: &mut self.symbols,
98                list,
99            }
100        } else {
101            unreachable!("Concurrent modification or corruption a DocBuilder")
102        }
103    }
104
105    /// Turns the builder into a `Doc`.
106    pub fn build(self) -> Doc {
107        Doc::new(self.symbols, self.root)
108    }
109}
110
111/// Builds an inner object or map within a `Doc` or another element.
112///
113/// A builder can either be obtained via [DocBuilder::root_object_builder](DocBuilder::root_object_builder)
114/// or using either [ObjectBuilder::put_object](ObjectBuilder::put_object) - to place an inner
115/// object in another or via [ListBuilder::append_object](ListBuilder::append_object) to add an
116/// object to a list.
117pub struct ObjectBuilder<'a> {
118    symbols: &'a mut SymbolTable,
119    map: &'a mut SymbolMap<Node>,
120}
121
122impl<'a> ObjectBuilder<'a> {
123    /// Places an integer value within the object being built.
124    ///
125    /// # Errors
126    /// If the internal symbol table overflows, an error is returned.
127    ///
128    /// # Example
129    /// ```
130    /// # use jupiter::infograph::builder::DocBuilder;
131    /// let mut builder = DocBuilder::new();
132    /// let mut obj_builder = builder.root_object_builder();
133    ///
134    /// obj_builder.put_int("Test", 42).unwrap();
135    ///
136    /// let doc = builder.build();
137    /// assert_eq!(doc.root().query("Test").as_int().unwrap(), 42);
138    /// ```
139    pub fn put_int(&mut self, key: impl AsRef<str>, value: i64) -> anyhow::Result<()> {
140        let symbol = self.symbols.find_or_create(key)?;
141        self.insert_int(symbol, value);
142        Ok(())
143    }
144
145    /// Places an integer value within the object being built using a `Symbol`which has been looked
146    /// up previously.
147    ///
148    /// # Example
149    /// ```
150    /// # use jupiter::infograph::builder::DocBuilder;
151    /// let mut builder = DocBuilder::new();
152    /// let key = builder.resolve("Test").unwrap();
153    /// let mut obj_builder = builder.root_object_builder();
154    ///
155    /// obj_builder.insert_int(key, 42);
156    ///
157    /// let doc = builder.build();
158    /// assert_eq!(doc.root().query("Test").as_int().unwrap(), 42);
159    /// ```
160    pub fn insert_int(&mut self, key: Symbol, value: i64) {
161        self.map.put(key, Node::Integer(value));
162    }
163
164    /// Places a string value within the object being built.
165    ///
166    /// # Errors
167    /// If the internal symbol table overflows, an error is returned.
168    ///
169    /// # Example
170    /// ```
171    /// # use jupiter::infograph::builder::DocBuilder;
172    /// let mut builder = DocBuilder::new();
173    /// let mut obj_builder = builder.root_object_builder();
174    ///
175    /// obj_builder.put_string("Test", "Foo").unwrap();
176    ///
177    /// let doc = builder.build();
178    /// assert_eq!(doc.root().query("Test").as_str().unwrap(), "Foo");
179    /// ```
180    pub fn put_string(
181        &mut self,
182        key: impl AsRef<str>,
183        value: impl AsRef<str>,
184    ) -> anyhow::Result<()> {
185        let symbol = self.symbols.find_or_create(key)?;
186        self.insert_string(symbol, value);
187
188        Ok(())
189    }
190
191    /// Places a string value within the object being built using a `Symbol`which has been looked
192    /// up previously.
193    ///
194    /// # Example
195    /// ```
196    /// # use jupiter::infograph::builder::DocBuilder;
197    /// let mut builder = DocBuilder::new();
198    /// let key = builder.resolve("Test").unwrap();
199    /// let mut obj_builder = builder.root_object_builder();
200    ///
201    /// obj_builder.insert_string(key, "Foo");
202    ///
203    /// let doc = builder.build();
204    /// assert_eq!(doc.root().query("Test").as_str().unwrap(), "Foo");
205    /// ```
206    pub fn insert_string(&mut self, key: Symbol, value: impl AsRef<str>) {
207        self.map.put(key, Node::from(value.as_ref()));
208    }
209
210    /// Places a bool value within the object being built.
211    ///
212    /// # Errors
213    /// If the internal symbol table overflows, an error is returned.
214    ///
215    /// # Example
216    /// ```
217    /// # use jupiter::infograph::builder::DocBuilder;
218    /// let mut builder = DocBuilder::new();
219    /// let mut obj_builder = builder.root_object_builder();
220    ///
221    /// obj_builder.put_bool("Test", true).unwrap();
222    ///
223    /// let doc = builder.build();
224    /// assert_eq!(doc.root().query("Test").as_bool(), true);
225    /// ```
226    pub fn put_bool(&mut self, key: impl AsRef<str>, value: bool) -> anyhow::Result<()> {
227        let symbol = self.symbols.find_or_create(key)?;
228        self.insert_bool(symbol, value);
229
230        Ok(())
231    }
232
233    /// Places bool value within the object being built using a `Symbol`which has been looked
234    /// up previously.
235    ///
236    /// # Example
237    /// ```
238    /// # use jupiter::infograph::builder::DocBuilder;
239    /// let mut builder = DocBuilder::new();
240    /// let key = builder.resolve("Test").unwrap();
241    /// let mut obj_builder = builder.root_object_builder();
242    ///
243    /// obj_builder.insert_bool(key, true);
244    ///
245    /// let doc = builder.build();
246    /// assert_eq!(doc.root().query("Test").as_bool(), true);
247    /// ```
248    pub fn insert_bool(&mut self, key: Symbol, value: bool) {
249        self.map.put(key, Node::Boolean(value));
250    }
251
252    /// Places a list value within the object being built. Returns the builder used to populate
253    /// the list.
254    ///
255    /// # Errors
256    /// If the internal symbol table overflows, an error is returned.
257    ///
258    /// # Example
259    /// ```
260    /// # use jupiter::infograph::builder::DocBuilder;
261    /// let mut builder = DocBuilder::new();
262    /// let mut obj_builder = builder.root_object_builder();
263    ///
264    /// let mut list_builder = obj_builder.put_list("Test").unwrap();
265    /// list_builder.append_int(1);
266    ///
267    /// let doc = builder.build();
268    /// assert_eq!(doc.root().query("Test").at(0).as_int().unwrap(), 1);
269    /// ```
270    pub fn put_list(&mut self, key: impl AsRef<str>) -> anyhow::Result<ListBuilder> {
271        let symbol = self.symbols.find_or_create(key)?;
272        Ok(self.insert_list(symbol))
273    }
274
275    /// Places a list value within the object being built using a `Symbol`which has been looked
276    /// up previously. Returns the builder used to populate the list.   
277    ///
278    /// # Example
279    /// ```
280    /// # use jupiter::infograph::builder::DocBuilder;
281    /// let mut builder = DocBuilder::new();
282    /// let key = builder.resolve("Test").unwrap();
283    /// let mut obj_builder = builder.root_object_builder();
284    ///
285    /// let mut list_builder = obj_builder.insert_list(key);
286    /// list_builder.append_int(1);
287    ///
288    /// let doc = builder.build();
289    /// assert_eq!(doc.root().query("Test").at(0).as_int().unwrap(), 1);
290    /// ```
291    pub fn insert_list(&mut self, key: Symbol) -> ListBuilder {
292        self.map.put(key, Node::List(Vec::new()));
293        if let Node::List(ref mut list) = self.map.get_mut(key).unwrap() {
294            ListBuilder {
295                symbols: self.symbols,
296                list,
297            }
298        } else {
299            unreachable!("Concurrent modification or corruption of the underlying map!")
300        }
301    }
302
303    /// Places an inner object within the object being built. Returns the builder used to populate
304    /// the list.
305    ///
306    /// # Errors
307    /// If the internal symbol table overflows, an error is returned.
308    ///
309    /// # Example
310    /// ```
311    /// # use jupiter::infograph::builder::DocBuilder;
312    /// let mut builder = DocBuilder::new();
313    /// let mut obj_builder = builder.root_object_builder();
314    ///
315    /// let mut inner_obj_builder = obj_builder.put_object("Test").unwrap();
316    /// inner_obj_builder.put_string("Foo", "Bar").unwrap();
317    ///
318    /// let doc = builder.build();
319    /// assert_eq!(doc.root().query("Test.Foo").as_str().unwrap(), "Bar");
320    /// ```
321    pub fn put_object(&mut self, key: impl AsRef<str>) -> anyhow::Result<ObjectBuilder> {
322        let symbol = self.symbols.find_or_create(key)?;
323        Ok(self.insert_object(symbol))
324    }
325
326    /// Places an inner object within the object being built using a `Symbol`which has been looked
327    /// up previously. Returns the builder used to populate the list.
328    ///
329    /// # Example
330    /// ```
331    /// # use jupiter::infograph::builder::DocBuilder;
332    /// let mut builder = DocBuilder::new();
333    /// let key = builder.resolve("Test").unwrap();
334    /// let mut obj_builder = builder.root_object_builder();
335    ///
336    /// let mut inner_obj_builder = obj_builder.insert_object(key);
337    /// inner_obj_builder.put_string("Foo", "Bar").unwrap();
338    ///
339    /// let doc = builder.build();
340    /// assert_eq!(doc.root().query("Test.Foo").as_str().unwrap(), "Bar");
341    /// ```
342    pub fn insert_object(&mut self, key: Symbol) -> ObjectBuilder {
343        self.map.put(key, Node::Object(SymbolMap::new()));
344        if let Node::Object(ref mut map) = self.map.get_mut(key).unwrap() {
345            ObjectBuilder {
346                symbols: self.symbols,
347                map,
348            }
349        } else {
350            unreachable!("Concurrent modification or corruption of the underlying map!")
351        }
352    }
353}
354
355/// Builds an inner list within a `Doc` or another element.
356///
357/// A builder can either be obtained via [DocBuilder::root_list_builder](DocBuilder::root_list_builder)
358/// or using either [ObjectBuilder::put_list](ObjectBuilder::put_list) - to place an inner
359/// list in another or via [ListBuilder::append_list](ListBuilder::append_list) to add a
360/// list as child element to a list.
361pub struct ListBuilder<'a> {
362    symbols: &'a mut SymbolTable,
363    list: &'a mut Vec<Node>,
364}
365
366impl<'a> ListBuilder<'a> {
367    /// Appends an integer value to the list being built.
368    ///
369    /// # Example
370    /// ```
371    /// # use jupiter::infograph::builder::DocBuilder;
372    /// let mut builder = DocBuilder::new();
373    /// let mut list_builder = builder.root_list_builder();
374    ///
375    /// list_builder.append_int(42);
376    ///
377    /// let doc = builder.build();
378    /// assert_eq!(doc.root().at(0).as_int().unwrap(), 42);
379    /// ```
380    pub fn append_int(&mut self, value: i64) {
381        self.list.push(Node::Integer(value));
382    }
383
384    /// Appends a string to the list being built.
385    ///
386    /// # Example
387    /// ```
388    /// # use jupiter::infograph::builder::DocBuilder;
389    /// let mut builder = DocBuilder::new();
390    /// let mut list_builder = builder.root_list_builder();
391    ///
392    /// list_builder.append_string("Test");
393    ///
394    /// let doc = builder.build();
395    /// assert_eq!(doc.root().at(0).as_str().unwrap(), "Test");
396    /// ```
397    pub fn append_string(&mut self, value: impl AsRef<str>) {
398        self.list.push(Node::from(value.as_ref()));
399    }
400
401    /// Appends a bool value to the list being built.
402    ///
403    /// # Example
404    /// ```
405    /// # use jupiter::infograph::builder::DocBuilder;
406    /// let mut builder = DocBuilder::new();
407    /// let mut list_builder = builder.root_list_builder();
408    ///
409    /// list_builder.append_bool(true);
410    ///
411    /// let doc = builder.build();
412    /// assert_eq!(doc.root().at(0).as_bool(), true);    
413    /// ```
414    pub fn append_bool(&mut self, value: bool) {
415        self.list.push(Node::Boolean(value));
416    }
417
418    /// Appends a child-list to the list being built. Returns the builder used to populate the list.
419    ///
420    /// Note that this will not join two lists but rather construct a list as child element and
421    /// append further items to this child list.
422    ///
423    /// # Example
424    /// ```
425    /// # use jupiter::infograph::builder::DocBuilder;
426    /// let mut builder = DocBuilder::new();
427    /// let mut list_builder = builder.root_list_builder();
428    ///
429    /// let mut child_list_builder = list_builder.append_list();
430    /// child_list_builder.append_int(42);
431    ///
432    /// let doc = builder.build();
433    /// assert_eq!(doc.root().len(), 1);    
434    /// assert_eq!(doc.root().at(0).len(), 1);    
435    /// assert_eq!(doc.root().at(0).at(0).as_int().unwrap(), 42);
436    /// ```
437    pub fn append_list(&mut self) -> ListBuilder {
438        self.list.push(Node::List(Vec::new()));
439        if let Node::List(ref mut list) = self.list.last_mut().unwrap() {
440            ListBuilder {
441                symbols: self.symbols,
442                list,
443            }
444        } else {
445            unreachable!("Concurrent modification or corruption of the underlying list!")
446        }
447    }
448
449    /// Appends a child object to the list being built. Returns the builder used to populate the
450    /// object.
451    ///
452    /// # Example
453    /// ```
454    /// # use jupiter::infograph::builder::DocBuilder;
455    /// let mut builder = DocBuilder::new();
456    /// let mut list_builder = builder.root_list_builder();
457    ///
458    /// let mut obj_builder = list_builder.append_object();
459    /// obj_builder.put_string("Foo", "Bar").unwrap();
460    ///
461    /// let doc = builder.build();
462    /// assert_eq!(doc.root().len(), 1);    
463    /// assert_eq!(doc.root().at(0).query("Foo").as_str().unwrap(), "Bar");
464    /// ```
465    pub fn append_object(&mut self) -> ObjectBuilder {
466        self.list.push(Node::Object(SymbolMap::new()));
467        if let Node::Object(ref mut map) = self.list.last_mut().unwrap() {
468            ObjectBuilder {
469                symbols: self.symbols,
470                map,
471            }
472        } else {
473            unreachable!("Concurrent modification or corruption of the underlying list!")
474        }
475    }
476}