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}