Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica
Skip to main content

eidetica/entry/
builder.rs

1//! Builder for creating Entry instances.
2
3use std::collections::HashSet;
4
5use rand::Rng;
6
7use super::{ENTRY_VERSION, Entry, EntryError, ID, RawData, SubTreeNode, TreeNode};
8use crate::{Result, auth::types::SigInfo, constants::ROOT, crdt::Doc, store::StoreError};
9
10/// A builder for creating `Entry` instances.
11///
12/// `EntryBuilder` allows mutable construction of an entry's content.
13/// Once finalized with the `build()` method, it produces an immutable `Entry`
14/// with a deterministically calculated ID.
15///
16/// # Parameter Type Efficiency
17///
18/// The builder uses `ID` for parent references and `impl Into<Vec<u8>>` for payload data.
19/// Payloads are opaque bytes — each `Store` owns its own encoding (JSON, CBOR, raw, etc.).
20///
21/// ```ignore
22/// use eidetica::entry::ID;
23///
24/// let entry = Entry::builder(ID::from_bytes("root_id"))
25///     .add_parent(ID::from_bytes("parent1"))
26///     .set_subtree_data("users", b"user_data".to_vec())
27///     .build();
28/// ```
29///
30/// # Mutable Construction
31///
32/// The builder provides two patterns for construction:
33/// 1. Ownership chaining: Each method returns `self` for chained calls.
34///    ```
35///    # use eidetica::Entry;
36///    # use eidetica::entry::ID;
37///    let entry = Entry::builder(ID::from_bytes("root_id"))
38///        .set_subtree_data("users", b"user_data".to_vec())
39///        .add_parent(ID::from_bytes("parent_id"))
40///        .build();
41///    ```
42///
43/// 2. Mutable reference: Methods ending in `_mut` modify the builder in place.
44///    ```
45///    # use eidetica::Entry;
46///    # use eidetica::entry::ID;
47///    let mut builder = Entry::builder(ID::from_bytes("root_id"));
48///    builder.set_subtree_data_mut("users", b"user_data".to_vec());
49///    builder.add_parent_mut(ID::from_bytes("parent_id"));
50///    let entry = builder.build();
51///    ```
52///
53/// # Example
54///
55/// ```
56/// use eidetica::Entry;
57/// use eidetica::entry::ID;
58///
59/// // Create a builder for a regular entry
60/// let entry = Entry::builder(ID::from_bytes("root_id"))
61///     .add_parent(ID::from_bytes("parent1"))
62///     .set_subtree_data("users", b"user_data".to_vec())
63///     .build();
64///
65/// // Create a builder for a top-level root entry
66/// let root_entry = Entry::root_builder()
67///     .set_subtree_data("users", b"initial_user_data".to_vec())
68///     .build();
69/// ```
70#[derive(Clone, Debug)]
71pub struct EntryBuilder {
72    pub(super) tree: TreeNode,
73    pub(super) subtrees: Vec<SubTreeNode>,
74    pub(super) sig: SigInfo,
75}
76
77impl EntryBuilder {
78    /// Creates a new `EntryBuilder` for an entry associated with a specific tree root.
79    ///
80    /// # Arguments
81    /// * `root` - The `ID` of the root `Entry` of the tree this entry will belong to.
82    ///
83    /// Note: It's generally preferred to use the static `Entry::builder()` method
84    /// instead of calling this constructor directly.
85    pub fn new(root: ID) -> Self {
86        Self {
87            tree: TreeNode {
88                root: if root.is_empty() { None } else { Some(root) },
89                parents: Vec::new(),
90                metadata: None,
91                height: 0,
92            },
93            subtrees: Vec::new(),
94            sig: SigInfo::default(),
95        }
96    }
97
98    /// Creates a new `EntryBuilder` for a top-level (root) entry for a new tree.
99    ///
100    /// Root entries have an empty string as their `root` ID and include a special ROOT subtree marker.
101    /// This method is typically used when creating a new tree.
102    ///
103    /// Note: It's generally preferred to use the static `Entry::root_builder()` method
104    /// instead of calling this constructor directly.
105    pub fn new_top_level() -> Self {
106        let mut builder = Self::new(ID::default());
107        // Add a special subtree that identifies this as a root entry
108        builder.set_subtree_data_mut(ROOT, b"");
109
110        // Add random entropy to metadata to ensure unique IDs for each root entry
111        let entropy: u64 = rand::thread_rng().r#gen();
112        let metadata_json = format!(r#"{{"entropy":{entropy}}}"#);
113        builder.set_metadata_mut(metadata_json);
114
115        builder
116    }
117
118    /// Set the authentication information for this entry.
119    ///
120    /// # Arguments
121    /// * `auth` - The authentication information including key ID and optional signature
122    pub fn set_sig(mut self, sig: SigInfo) -> Self {
123        self.sig = sig;
124        self
125    }
126
127    /// Mutable reference version of set_auth.
128    /// Set the authentication information for this entry.
129    ///
130    /// # Arguments
131    /// * `auth` - The authentication information including key ID and optional signature
132    pub fn set_sig_mut(&mut self, sig: SigInfo) -> &mut Self {
133        self.sig = sig;
134        self
135    }
136
137    /// Get the names of all subtrees this entry builder contains data for.
138    /// The names are returned in alphabetical order.
139    pub fn subtrees(&self) -> Vec<String> {
140        self.subtrees
141            .iter()
142            .map(|subtree| subtree.name.clone())
143            .collect()
144    }
145
146    /// Get the `RawData` for a specific named subtree within this entry builder.
147    ///
148    /// Returns an error if the subtree is not found or if the subtree exists but has no data (`None`).
149    pub fn data(&self, subtree_name: impl AsRef<str>) -> Result<&RawData> {
150        self.subtrees
151            .iter()
152            .find(|node| node.name == subtree_name.as_ref())
153            .and_then(|node| node.data.as_ref())
154            .ok_or_else(|| {
155                StoreError::KeyNotFound {
156                    store: "entry".to_string(),
157                    key: subtree_name.as_ref().to_string(),
158                }
159                .into()
160            })
161    }
162
163    /// Get the IDs of the parent entries for the main tree.
164    /// The parent IDs are returned in alphabetical order.
165    pub fn parents(&self) -> Result<Vec<ID>> {
166        Ok(self.tree.parents.clone())
167    }
168
169    /// Get the IDs of the parent entries specific to a named subtree's history.
170    /// The parent IDs are returned in alphabetical order.
171    pub fn subtree_parents(&self, subtree_name: impl AsRef<str>) -> Result<Vec<ID>> {
172        self.subtrees
173            .iter()
174            .find(|node| node.name == subtree_name.as_ref())
175            .map(|node| node.parents.clone())
176            .ok_or_else(|| {
177                StoreError::KeyNotFound {
178                    store: "entry".to_string(),
179                    key: subtree_name.as_ref().to_string(),
180                }
181                .into()
182            })
183    }
184
185    /// Sort a list of parent IDs in alphabetical order.
186    fn sort_parents_list(parents: &mut [ID]) {
187        parents.sort();
188    }
189
190    /// Sort the list of subtrees in alphabetical order by name.
191    ///
192    /// This is important for ensuring entries with the same content have the same ID.
193    fn sort_subtrees_list(&mut self) {
194        self.subtrees.sort_by(|a, b| a.name.cmp(&b.name));
195    }
196
197    /// Sets data for a named subtree, creating it if it doesn't exist.
198    /// The list of subtrees will be sorted by name when `build()` is called.
199    ///
200    /// # Arguments
201    /// * `name` - The name of the subtree (e.g., "users", "products").
202    /// * `data` - Opaque payload bytes for this entry in the named subtree. Each `Store`
203    ///   owns the format of its own payload; the entry layer treats the bytes as opaque.
204    pub fn set_subtree_data(mut self, name: impl Into<String>, data: impl Into<RawData>) -> Self {
205        self.set_subtree_data_mut(name, data);
206        self
207    }
208
209    /// Mutable reference version of set_subtree_data.
210    /// Sets data for a named subtree, creating it if it doesn't exist.
211    /// The list of subtrees will be sorted by name when `build()` is called.
212    ///
213    /// # Arguments
214    /// * `name` - The name of the subtree (e.g., "users", "products").
215    /// * `data` - Opaque payload bytes for this entry in the named subtree.
216    pub fn set_subtree_data_mut(
217        &mut self,
218        name: impl Into<String>,
219        data: impl Into<RawData>,
220    ) -> &mut Self {
221        let name = name.into();
222        let data = data.into();
223        if let Some(node) = self.subtrees.iter_mut().find(|node| node.name == name) {
224            node.data = Some(data);
225        } else {
226            self.subtrees.push(SubTreeNode {
227                name,
228                data: Some(data),
229                parents: vec![],
230                height: None,
231            });
232        }
233        self
234    }
235
236    /// Removes subtrees that have empty data.
237    ///
238    /// This removes subtrees with `Some("")` (actual empty data) and subtrees with `None`
239    /// (no data changes) UNLESS the subtree is referenced in the `_index` subtree's data.
240    ///
241    /// When `_index` is updated for a subtree, that subtree must appear in the Entry.
242    /// This is marked by having `None` data and being referenced in `_index`.
243    ///
244    /// This is useful for cleaning up entries before building.
245    ///
246    /// # Errors
247    ///
248    /// Returns an error if the `_index` subtree data exists but cannot be deserialized.
249    pub fn remove_empty_subtrees(mut self) -> Result<Self> {
250        // Get the set of subtrees referenced in _index
251        let index_subtrees = self.get_index_referenced_subtrees()?;
252
253        self.subtrees.retain(|subtree| match &subtree.data {
254            None => {
255                // Preserve None only if this subtree is referenced in _index
256                index_subtrees.contains(&subtree.name)
257            }
258            Some(d) => !d.is_empty(), // Remove only if Some with empty string
259        });
260        Ok(self)
261    }
262
263    /// Mutable reference version of remove_empty_subtrees.
264    ///
265    /// Removes subtrees with `Some("")` and subtrees with `None` unless referenced in `_index`.
266    ///
267    /// # Errors
268    ///
269    /// Returns an error if the `_index` subtree data exists but cannot be deserialized.
270    pub fn remove_empty_subtrees_mut(&mut self) -> Result<&mut Self> {
271        // Get the set of subtrees referenced in _index
272        let index_subtrees = self.get_index_referenced_subtrees()?;
273
274        self.subtrees.retain(|subtree| match &subtree.data {
275            None => {
276                // Preserve None only if this subtree is referenced in _index
277                index_subtrees.contains(&subtree.name)
278            }
279            Some(d) => !d.is_empty(), // Remove only if Some with empty string
280        });
281        Ok(self)
282    }
283
284    /// Get the set of subtree names that are referenced in the `_index` subtree's local data.
285    ///
286    /// Returns a set of subtree names that have entries in the local `_index` subtree.
287    /// This is used to determine which subtrees with `None` data should be preserved.
288    ///
289    /// # Errors
290    ///
291    /// Returns an error if the `_index` subtree data exists but cannot be deserialized.
292    fn get_index_referenced_subtrees(&self) -> Result<HashSet<String>> {
293        let mut result = HashSet::new();
294
295        // Find the _index subtree's local data
296        if let Some(index_node) = self.subtrees.iter().find(|node| node.name == "_index") {
297            // If _index has data, deserialize it and get all keys
298            if let Some(data) = &index_node.data {
299                // _index is a first-party subtree owned by the entry layer, so JSON is
300                // the canonical encoding here.
301                let doc = serde_json::from_slice::<Doc>(data).map_err(|e| {
302                    EntryError::InvalidIndexData {
303                        reason: format!(
304                            "Failed to deserialize _index subtree data: {} (data preview: {:?})",
305                            e,
306                            &data[..data.len().min(100)]
307                        ),
308                    }
309                })?;
310
311                for key in doc.keys() {
312                    result.insert(key.to_string());
313                }
314            }
315        }
316
317        Ok(result)
318    }
319
320    /// Set the root ID for this entry.
321    ///
322    /// # Arguments
323    /// * `root` - The ID of the root `Entry` of the tree this entry will belong to.
324    ///
325    /// # Returns
326    /// A mutable reference to self for method chaining.
327    pub fn set_root(mut self, root: ID) -> Self {
328        self.tree.root = if root.is_empty() { None } else { Some(root) };
329        self
330    }
331
332    /// Mutable reference version of set_root.
333    /// Set the root ID for this entry.
334    ///
335    /// # Arguments
336    /// * `root` - The ID of the root `Entry` of the tree this entry will belong to.
337    ///
338    /// # Returns
339    /// A mutable reference to self for method chaining.
340    pub fn set_root_mut(&mut self, root: ID) -> &mut Self {
341        self.tree.root = if root.is_empty() { None } else { Some(root) };
342        self
343    }
344
345    /// Set the parent IDs for the main tree history.
346    /// The provided vector will be sorted alphabetically during the `build()` process.
347    pub fn set_parents(mut self, parents: Vec<ID>) -> Self {
348        self.tree.parents = parents;
349        self
350    }
351
352    /// Mutable reference version of set_parents.
353    /// Set the parent IDs for the main tree history.
354    /// The provided vector will be sorted alphabetically during the `build()` process.
355    pub fn set_parents_mut(&mut self, parents: Vec<ID>) -> &mut Self {
356        self.tree.parents = parents;
357        self
358    }
359
360    /// Add a single parent ID to the main tree history.
361    /// Parents will be sorted and duplicates handled during the `build()` process.
362    pub fn add_parent(mut self, parent_id: ID) -> Self {
363        self.tree.parents.push(parent_id);
364        self
365    }
366
367    /// Mutable reference version of add_parent.
368    /// Add a single parent ID to the main tree history.
369    /// Parents will be sorted and duplicates handled during the `build()` process.
370    pub fn add_parent_mut(&mut self, parent_id: ID) -> &mut Self {
371        self.tree.parents.push(parent_id);
372        self
373    }
374
375    /// Get a reference to the current parent IDs for the main tree history.
376    pub fn get_parents(&self) -> Option<&Vec<ID>> {
377        if self.tree.parents.is_empty() {
378            None
379        } else {
380            Some(&self.tree.parents)
381        }
382    }
383
384    /// Set the parent IDs for a specific named subtree's history.
385    /// The provided vector will be sorted alphabetically and de-duplicated during the `build()` process.
386    /// If the subtree does not exist, it will be created with empty data ("{}").
387    /// The list of subtrees will be sorted by name when `build()` is called.
388    pub fn set_subtree_parents(
389        mut self,
390        subtree_name: impl Into<String>,
391        parents: Vec<ID>,
392    ) -> Self {
393        let subtree_name = subtree_name.into();
394        if let Some(node) = self
395            .subtrees
396            .iter_mut()
397            .find(|node| node.name == subtree_name)
398        {
399            node.parents = parents;
400        } else {
401            // Create new SubTreeNode if it doesn't exist, then set parents
402            self.subtrees.push(SubTreeNode {
403                name: subtree_name,
404                data: None,
405                parents,
406                height: None,
407            });
408        }
409        self
410    }
411
412    /// Mutable reference version of set_subtree_parents.
413    /// Set the parent IDs for a specific named subtree's history.
414    /// The provided vector will be sorted alphabetically and de-duplicated during the `build()` process.
415    /// If the subtree does not exist, it will be created with no data (`None`).
416    /// The list of subtrees will be sorted by name when `build()` is called.
417    pub fn set_subtree_parents_mut(
418        &mut self,
419        subtree_name: impl Into<String>,
420        parents: Vec<ID>,
421    ) -> &mut Self {
422        let subtree_name = subtree_name.into();
423        if let Some(node) = self
424            .subtrees
425            .iter_mut()
426            .find(|node| node.name == subtree_name)
427        {
428            node.parents = parents;
429        } else {
430            // Create new SubTreeNode if it doesn't exist, then set parents
431            self.subtrees.push(SubTreeNode {
432                name: subtree_name,
433                data: None,
434                parents,
435                height: None,
436            });
437        }
438        self
439    }
440
441    /// Add a single parent ID to a specific named subtree's history.
442    /// If the subtree does not exist, it will be created with no data (`None`).
443    /// Parent IDs will be sorted and de-duplicated during the `build()` process.
444    /// The list of subtrees will be sorted by name when `build()` is called.
445    pub fn add_subtree_parent(mut self, subtree_name: impl Into<String>, parent_id: ID) -> Self {
446        let subtree_name = subtree_name.into();
447        if let Some(node) = self
448            .subtrees
449            .iter_mut()
450            .find(|node| node.name == subtree_name)
451        {
452            node.parents.push(parent_id);
453        } else {
454            self.subtrees.push(SubTreeNode {
455                name: subtree_name,
456                data: None,
457                parents: vec![parent_id],
458                height: None,
459            });
460        }
461        self
462    }
463
464    /// Mutable reference version of add_subtree_parent.
465    /// Add a single parent ID to a specific named subtree's history.
466    /// If the subtree does not exist, it will be created with no data (`None`).
467    /// Parent IDs will be sorted and de-duplicated during the `build()` process.
468    /// The list of subtrees will be sorted by name when `build()` is called.
469    pub fn add_subtree_parent_mut(
470        &mut self,
471        subtree_name: impl Into<String>,
472        parent_id: ID,
473    ) -> &mut Self {
474        let subtree_name = subtree_name.into();
475        if let Some(node) = self
476            .subtrees
477            .iter_mut()
478            .find(|node| node.name == subtree_name)
479        {
480            node.parents.push(parent_id);
481        } else {
482            self.subtrees.push(SubTreeNode {
483                name: subtree_name,
484                data: None,
485                parents: vec![parent_id],
486                height: None,
487            });
488        }
489        self
490    }
491
492    /// Set the metadata for this entry's tree node.
493    ///
494    /// Metadata is optional information attached to an entry that is not part of the
495    /// main data model and is not merged between entries. It's used primarily for
496    /// improving efficiency of operations and for experimentation.
497    ///
498    /// For example, metadata can contain references to the current tips of the settings
499    /// subtree, allowing for efficient verification in sparse checkout scenarios.
500    ///
501    /// # Arguments
502    /// * `metadata` - Opaque metadata bytes for the main tree node.
503    ///
504    /// # Returns
505    /// Self for method chaining.
506    pub fn set_metadata(mut self, metadata: impl Into<RawData>) -> Self {
507        self.tree.metadata = Some(metadata.into());
508        self
509    }
510
511    /// Mutable reference version of set_metadata.
512    /// Set the metadata for this entry's tree node.
513    ///
514    /// # Arguments
515    /// * `metadata` - Opaque metadata bytes for the main tree node.
516    ///
517    /// # Returns
518    /// A mutable reference to self for method chaining.
519    pub fn set_metadata_mut(&mut self, metadata: impl Into<RawData>) -> &mut Self {
520        self.tree.metadata = Some(metadata.into());
521        self
522    }
523
524    /// Get the current metadata value for this entry builder.
525    ///
526    /// Metadata is optional information attached to an entry that is not part of the
527    /// main data model and is not merged between entries. It's used primarily for
528    /// improving efficiency of operations and for experimentation.
529    ///
530    /// # Returns
531    ///
532    /// Returns `Some(&RawData)` containing the serialized metadata if present,
533    /// or `None` if no metadata has been set.
534    ///
535    /// # Example
536    ///
537    /// ```ignore
538    /// use eidetica::entry::ID;
539    /// let builder = Entry::builder(ID::from_bytes("root_id"));
540    /// assert!(builder.metadata().is_none());
541    ///
542    /// let builder = builder.set_metadata(br#"{"custom": "data"}"#.to_vec());
543    /// assert!(builder.metadata().is_some());
544    /// ```
545    pub fn metadata(&self) -> Option<&RawData> {
546        self.tree.metadata.as_ref()
547    }
548
549    /// Set the height for this entry in the main tree DAG.
550    ///
551    /// # Arguments
552    /// * `height` - The height value for this entry
553    pub fn set_height(mut self, height: u64) -> Self {
554        self.tree.height = height;
555        self
556    }
557
558    /// Mutable reference version of set_height.
559    pub fn set_height_mut(&mut self, height: u64) -> &mut Self {
560        self.tree.height = height;
561        self
562    }
563
564    /// Set the height for this entry in a specific subtree's DAG.
565    ///
566    /// If the subtree does not exist, it will be created with no data (`None`).
567    ///
568    /// # Arguments
569    /// * `subtree_name` - The name of the subtree
570    /// * `height` - The height value for this entry in the subtree
571    pub fn set_subtree_height(
572        mut self,
573        subtree_name: impl Into<String>,
574        height: Option<u64>,
575    ) -> Self {
576        let subtree_name = subtree_name.into();
577        if let Some(node) = self
578            .subtrees
579            .iter_mut()
580            .find(|node| node.name == subtree_name)
581        {
582            node.height = height;
583        } else {
584            self.subtrees.push(SubTreeNode {
585                name: subtree_name,
586                data: None,
587                parents: vec![],
588                height,
589            });
590        }
591        self
592    }
593
594    /// Mutable reference version of set_subtree_height.
595    pub fn set_subtree_height_mut(
596        &mut self,
597        subtree_name: impl Into<String>,
598        height: Option<u64>,
599    ) -> &mut Self {
600        let subtree_name = subtree_name.into();
601        if let Some(node) = self
602            .subtrees
603            .iter_mut()
604            .find(|node| node.name == subtree_name)
605        {
606            node.height = height;
607        } else {
608            self.subtrees.push(SubTreeNode {
609                name: subtree_name,
610                data: None,
611                parents: vec![],
612                height,
613            });
614        }
615        self
616    }
617
618    /// Build and return the final immutable `Entry`.
619    ///
620    /// This method:
621    /// 1. Sorts all parent lists in both the main tree and subtrees
622    /// 2. Sorts the subtrees list by name
623    /// 3. Removes any empty subtrees
624    /// 4. Creates the immutable `Entry`
625    /// 5. Validates the entry structure
626    /// 6. Returns the validated entry or an error
627    ///
628    /// After calling this method, the builder is consumed and cannot be used again.
629    /// The returned `Entry` is immutable and its parts cannot be modified.
630    ///
631    /// # Returns
632    ///
633    /// - `Ok(Entry)` if the entry is structurally valid
634    /// - `Err(crate::Error)` if the entry fails validation (e.g., root entry with parents)
635    pub fn build(mut self) -> Result<Entry> {
636        // Sort parent lists (if any)
637        Self::sort_parents_list(&mut self.tree.parents);
638        for subtree in &mut self.subtrees {
639            Self::sort_parents_list(&mut subtree.parents);
640        }
641
642        // Deduplicate parents
643        self.tree.parents.dedup();
644        for subtree in &mut self.subtrees {
645            subtree.parents.dedup();
646        }
647
648        // Sort subtrees
649        self.sort_subtrees_list();
650
651        let entry = Entry {
652            version: ENTRY_VERSION,
653            tree: self.tree,
654            subtrees: self.subtrees,
655            sig: self.sig,
656        };
657
658        // Validate the built entry before returning
659        entry.validate()?;
660
661        Ok(entry)
662    }
663}