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}