willow_data_model/entry/
entry.rs

1use core::cmp::Ordering;
2
3#[cfg(feature = "dev")]
4use arbitrary::Arbitrary;
5
6use crate::prelude::*;
7
8/// The metadata associated with each Willow [Payload](https://willowprotocol.org/specs/data-model/index.html#Payload) string.
9///
10/// [Entries](https://willowprotocol.org/specs/data-model/index.html#Entry) are the central concept in Willow. In order to make any bytestring of data accessible to Willow, you need to create an Entry describing its metadata. Specifically, an Entry consists of
11///
12/// - a [namespace_id](https://willowprotocol.org/specs/data-model/index.html#entry_namespace_id) (roughly, this addresses a universe of Willow data, fully independent from all data (i.e., Entries) of different namespace ids) of type `N`,
13/// - a [subspace_id](https://willowprotocol.org/specs/data-model/index.html#entry_subspace_id) (roughly, a fully indendent part of a namespace, typically subspaces correspond to individual users) of type `S`,
14/// - a [path](https://willowprotocol.org/specs/data-model/index.html#entry_path) (roughly, a file-system-like way of arranging payloads hierarchically within a subspace) of type [`Path`],
15/// - a [timestamp](https://willowprotocol.org/specs/data-model/index.html#entry_timestamp) (newer Entries can overwrite certain older Entries),
16/// - a [payload_length](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) (the length of the payload string), and
17/// - a [payload_digest](https://willowprotocol.org/specs/data-model/index.html#entry_payload_digest) (a secure hash of the payload string being inserted into Willow).
18///
19/// To access these six fields, use the methods of the [`Entrylike`] trait (which [`Entry`] implements). The [`EntrylikeExt`] trait provides additional helper methods, for example, methods to check which Entries can delete which other Entries.
20///
21/// To create Entries, use the [`Entry::builder`] or [`Entry::prefilled_builder`] functions.
22///
23/// # Example
24///
25/// ```
26/// use willow_data_model::prelude::*;
27///
28/// let entry = Entry::builder()
29///     .namespace_id("family")
30///     .subspace_id("alfie")
31///     .path(Path::<4, 4, 4>::new())
32///     .timestamp(12345)
33///     .payload_digest("some_hash")
34///     .payload_length(17)
35///     .build().unwrap();
36///
37/// assert_eq!(*entry.wdm_subspace_id(), "alfie");
38///
39/// let newer = Entry::prefilled_builder(&entry).timestamp(99999).build().unwrap();
40/// assert!(newer.prunes(&entry));
41/// ```
42///
43/// [Spec definition](https://willowprotocol.org/specs/data-model/index.html#Entry).
44#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
45#[cfg_attr(feature = "dev", derive(Arbitrary))]
46pub struct Entry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> {
47    pub(crate) namespace_id: N,
48    pub(crate) subspace_id: S,
49    pub(crate) path: Path<MCL, MCC, MPL>,
50    pub(crate) timestamp: Timestamp,
51    pub(crate) payload_length: u64,
52    pub(crate) payload_digest: PD,
53}
54
55impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
56    Entry<MCL, MCC, MPL, N, S, PD>
57{
58    /// Creates a builder for [`Entry`].
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// use willow_data_model::prelude::*;
64    ///
65    /// // Supplying incomplete data errors.
66    /// assert!(
67    ///     Entry::builder()
68    ///     .path(Path::<4, 4, 4>::new())
69    ///     .namespace_id("family")
70    ///     .subspace_id("alfie")
71    ///     .payload_digest("some_hash")
72    ///     // timestamp and payload_length are missing!
73    ///     .build().is_err()
74    /// );
75    ///
76    /// // Supplying all necessary data yields an entry.
77    /// let entry = Entry::builder()
78    ///     .namespace_id("family")
79    ///     .subspace_id("alfie")
80    ///     .path(Path::<4, 4, 4>::new())
81    ///     .timestamp(12345)
82    ///     .payload_digest("some_hash")
83    ///     .payload_length(17)
84    ///     .build().unwrap();
85    ///
86    /// assert_eq!(*entry.wdm_subspace_id(), "alfie");
87    /// ```
88    pub fn builder() -> EntryBuilder<MCL, MCC, MPL, N, S, PD> {
89        EntryBuilder::create_empty()
90    }
91
92    /// Creates a builder which is prefilled with the data from some other entry.
93    ///
94    /// Use this function to create modified copies of entries.
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// use willow_data_model::prelude::*;
100    ///
101    /// // Supplying all necessary data yields an entry.
102    /// let first_entry = Entry::builder()
103    ///     .namespace_id("family")
104    ///     .subspace_id("alfie")
105    ///     .path(Path::<4, 4, 4>::new())
106    ///     .timestamp(12345)
107    ///     .payload_digest("some_hash")
108    ///     .payload_length(17)
109    ///     .build().unwrap();
110    ///
111    /// assert_eq!(*first_entry.wdm_payload_digest(), "some_hash");
112    ///
113    /// let second_entry = Entry::prefilled_builder(&first_entry)
114    ///     .timestamp(67890)
115    ///     .payload_digest("another_hash")
116    ///     .payload_length(4)
117    ///     .build().unwrap();
118    ///
119    /// assert_eq!(*second_entry.wdm_payload_digest(), "another_hash");
120    /// ```
121    pub fn prefilled_builder<E>(source: &E) -> EntryBuilder<MCL, MCC, MPL, N, S, PD>
122    where
123        E: Entrylike<MCL, MCC, MPL, N, S, PD>,
124        N: Clone,
125        S: Clone,
126        PD: Clone,
127    {
128        let mut builder = Self::builder();
129
130        builder
131            .namespace_id(source.wdm_namespace_id().clone())
132            .subspace_id(source.wdm_subspace_id().clone())
133            .path(source.wdm_path().clone())
134            .timestamp(source.wdm_timestamp())
135            .payload_digest(source.wdm_payload_digest().clone())
136            .payload_length(source.wdm_payload_length());
137
138        builder
139    }
140}
141
142impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Keylike<MCL, MCC, MPL, S>
143    for Entry<MCL, MCC, MPL, N, S, PD>
144{
145    fn wdm_subspace_id(&self) -> &S {
146        &self.subspace_id
147    }
148
149    fn wdm_path(&self) -> &Path<MCL, MCC, MPL> {
150        &self.path
151    }
152}
153
154impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
155    Entrylike<MCL, MCC, MPL, N, S, PD> for Entry<MCL, MCC, MPL, N, S, PD>
156{
157    fn wdm_namespace_id(&self) -> &N {
158        &self.namespace_id
159    }
160
161    fn wdm_timestamp(&self) -> Timestamp {
162        self.timestamp
163    }
164
165    fn wdm_payload_length(&self) -> u64 {
166        self.payload_length
167    }
168
169    fn wdm_payload_digest(&self) -> &PD {
170        &self.payload_digest
171    }
172}
173
174/// A trait describing the metadata associated with each Willow [Payload](https://willowprotocol.org/specs/data-model/index.html#Payload) string.
175///
176/// [Entries](https://willowprotocol.org/specs/data-model/index.html#Entry) are the central concept in Willow. In order to make any bytestring of data accessible to Willow, you need to create an Entry describing its metadata. Specifically, an Entry consists of
177///
178/// - a [namespace_id](https://willowprotocol.org/specs/data-model/index.html#entry_namespace_id) (roughly, this addresses a universe of Willow data, fully independent from all data (i.e., Entries) of different namespace ids) of type `N`,
179/// - a [subspace_id](https://willowprotocol.org/specs/data-model/index.html#entry_subspace_id) (roughly, a fully indendent part of a namespace, typically subspaces correspond to individual users) of type `S`,
180/// - a [path](https://willowprotocol.org/specs/data-model/index.html#entry_path) (roughly, a file-system-like way of arranging payloads hierarchically within a subspace) of type [`Path`],
181/// - a [timestamp](https://willowprotocol.org/specs/data-model/index.html#entry_timestamp) (newer Entries can overwrite certain older Entries),
182/// - a [payload_length](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) (the length of the payload string), and
183/// - a [payload_digest](https://willowprotocol.org/specs/data-model/index.html#entry_payload_digest) (a secure hash of the payload string being inserted into Willow).
184///
185/// This trait can be implemented by all types which provide at least this information. We use this trait in order to be able to abstract over specific implementations of entries. If you want a concrete type for representing entries, use the [`Entry`] struct.
186pub trait Entrylike<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>:
187    Keylike<MCL, MCC, MPL, S>
188{
189    /// Returns the [namespace_id](https://willowprotocol.org/specs/data-model/index.html#entry_namespace_id) of `self`.
190    fn wdm_namespace_id(&self) -> &N;
191
192    /// Returns the [timestamp](https://willowprotocol.org/specs/data-model/index.html#entry_timestamp) of `self`.
193    fn wdm_timestamp(&self) -> Timestamp;
194
195    /// Returns the [payload_length](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) of `self`.
196    fn wdm_payload_length(&self) -> u64;
197
198    /// Returns the [payload_digest](https://willowprotocol.org/specs/data-model/index.html#entry_payload_digest) of `self`.
199    fn wdm_payload_digest(&self) -> &PD;
200}
201
202/// Methods for working with [`Entrylikes`](Entrylike).
203///
204/// This trait is automatically implemented by all types implmenting [`Entrylikes`](Entrylike).
205pub trait EntrylikeExt<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>:
206    Entrylike<MCL, MCC, MPL, N, S, PD>
207{
208    /// Returns whether `self` and `other` describe equal entries.
209    ///
210    /// # Examples
211    ///
212    /// ```
213    /// use willow_data_model::prelude::*;
214    /// use willow_data_model::prelude::*;
215    ///
216    /// let entry = Entry::builder()
217    ///     .namespace_id("family")
218    ///     .subspace_id("alfie")
219    ///     .path(Path::<4, 4, 4>::new())
220    ///     .timestamp(12345)
221    ///     .payload_digest("b")
222    ///     .payload_length(17)
223    ///     .build().unwrap();
224    ///
225    /// assert!(entry.entry_eq(&entry));
226    ///
227    /// let changed = Entry::prefilled_builder(&entry).timestamp(999999).build().unwrap();
228    /// assert!(!entry.entry_eq(&changed));
229    ///
230    /// ```
231    fn entry_eq<OtherEntry>(&self, other: &OtherEntry) -> bool
232    where
233        OtherEntry: Entrylike<MCL, MCC, MPL, N, S, PD>,
234        N: PartialEq,
235        S: PartialEq,
236        PD: PartialEq,
237    {
238        self.wdm_namespace_id() == other.wdm_namespace_id()
239            && self.wdm_subspace_id() == other.wdm_subspace_id()
240            && self.wdm_path() == other.wdm_path()
241            && self.wdm_timestamp() == other.wdm_timestamp()
242            && self.wdm_payload_digest() == other.wdm_payload_digest()
243            && self.wdm_payload_length() == other.wdm_payload_length()
244    }
245
246    /// Compares `self` to another entry by [timestamp](https://willowprotocol.org/specs/data-model/index.html#entry_timestamp), [payload_digest](https://willowprotocol.org/specs/data-model/index.html#entry_payload_digest) (in case of a tie), and [payload_length](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) third (in case of yet another tie). See also [`EntrylikeExt::is_newer_than`] and [`EntrylikeExt::is_older_than`].
247    ///
248    /// Comparing recency is primarily important to determine [which entries overwrite each other](https://willowprotocol.org/specs/data-model/index.html#prefix_pruning); the [`EntrylikeExt::prunes`] method checks for that directly.
249    ///
250    /// # Examples
251    ///
252    /// ```
253    /// use core::cmp::Ordering;
254    /// use willow_data_model::prelude::*;
255    ///
256    /// let entry = Entry::builder()
257    ///     .namespace_id("family")
258    ///     .subspace_id("alfie")
259    ///     .path(Path::<4, 4, 4>::new())
260    ///     .timestamp(12345)
261    ///     .payload_digest("b")
262    ///     .payload_length(17)
263    ///     .build().unwrap();
264    ///
265    /// assert_eq!(entry.cmp_recency(&entry), Ordering::Equal);
266    ///
267    /// let lesser_timestamp = Entry::prefilled_builder(&entry).timestamp(5).build().unwrap();
268    /// assert_eq!(entry.cmp_recency(&lesser_timestamp), Ordering::Greater);
269    ///
270    /// let lesser_digest = Entry::prefilled_builder(&entry).payload_digest("a").build().unwrap();
271    /// assert_eq!(entry.cmp_recency(&lesser_digest), Ordering::Greater);
272    ///
273    /// let lesser_length = Entry::prefilled_builder(&entry).payload_length(0).build().unwrap();
274    /// assert_eq!(entry.cmp_recency(&lesser_length), Ordering::Greater);
275    ///
276    /// let greater_timestamp = Entry::prefilled_builder(&entry).timestamp(999999).build().unwrap();
277    /// assert_eq!(entry.cmp_recency(&greater_timestamp), Ordering::Less);
278    ///
279    /// let greater_digest = Entry::prefilled_builder(&entry).payload_digest("c").build().unwrap();
280    /// assert_eq!(entry.cmp_recency(&greater_digest), Ordering::Less);
281    ///
282    /// let greater_length = Entry::prefilled_builder(&entry).payload_length(99).build().unwrap();
283    /// assert_eq!(entry.cmp_recency(&greater_length), Ordering::Less);
284    /// ```
285    ///
286    /// [Spec definition](https://willowprotocol.org/specs/data-model/index.html#entry_newer).
287    fn cmp_recency<OtherEntry>(&self, other: &OtherEntry) -> Ordering
288    where
289        OtherEntry: Entrylike<MCL, MCC, MPL, N, S, PD>,
290        PD: Ord,
291    {
292        self.wdm_timestamp()
293            .cmp(&other.wdm_timestamp())
294            .then_with(|| {
295                self.wdm_payload_digest()
296                    .cmp(other.wdm_payload_digest())
297                    .then_with(|| self.wdm_payload_length().cmp(&other.wdm_payload_length()))
298            })
299    }
300
301    /// Returns whether this entry is strictly [newer](https://willowprotocol.org/specs/data-model/index.html#entry_newer) than another entry. See also [`EntrylikeExt::cmp_recency`] and [`EntrylikeExt::is_older_than`].
302    ///
303    /// Comparing recency is primarily important to determine [which entries overwrite each other](https://willowprotocol.org/specs/data-model/index.html#prefix_pruning); the [`EntrylikeExt::prunes`] method checks for that directly.
304    ///
305    /// # Examples
306    ///
307    /// ```
308    /// use willow_data_model::prelude::*;
309    /// use willow_data_model::prelude::*;
310    ///
311    /// let entry = Entry::builder()
312    ///     .namespace_id("family")
313    ///     .subspace_id("alfie")
314    ///     .path(Path::<4, 4, 4>::new())
315    ///     .timestamp(12345)
316    ///     .payload_digest("b")
317    ///     .payload_length(17)
318    ///     .build().unwrap();
319    ///
320    /// assert!(!entry.is_newer_than(&entry));
321    ///
322    /// let lesser_timestamp = Entry::prefilled_builder(&entry).timestamp(5).build().unwrap();
323    /// assert!(entry.is_newer_than(&lesser_timestamp));
324    ///
325    /// let lesser_digest = Entry::prefilled_builder(&entry).payload_digest("a").build().unwrap();
326    /// assert!(entry.is_newer_than(&lesser_digest));
327    ///
328    /// let lesser_length = Entry::prefilled_builder(&entry).payload_length(0).build().unwrap();
329    /// assert!(entry.is_newer_than(&lesser_length));
330    ///
331    /// let greater_timestamp = Entry::prefilled_builder(&entry).timestamp(999999).build().unwrap();
332    /// assert!(!entry.is_newer_than(&greater_timestamp));
333    ///
334    /// let greater_digest = Entry::prefilled_builder(&entry).payload_digest("c").build().unwrap();
335    /// assert!(!entry.is_newer_than(&greater_digest));
336    ///
337    /// let greater_length = Entry::prefilled_builder(&entry).payload_length(99).build().unwrap();
338    /// assert!(!entry.is_newer_than(&greater_length));
339    /// ```
340    fn is_newer_than<OtherEntry>(&self, other: &OtherEntry) -> bool
341    where
342        OtherEntry: Entrylike<MCL, MCC, MPL, N, S, PD>,
343        PD: Ord,
344    {
345        self.cmp_recency(other) == Ordering::Greater
346    }
347
348    /// Returns whether this entry is strictly [older](https://willowprotocol.org/specs/data-model/index.html#entry_newer) than another entry. See also [`EntrylikeExt::cmp_recency`] and [`EntrylikeExt::is_newer_than`].
349    ///
350    /// Comparing recency is primarily important to determine [which entries overwrite each other](https://willowprotocol.org/specs/data-model/index.html#prefix_pruning); the [`EntrylikeExt::prunes`] method checks for that directly.
351    ///
352    /// # Examples
353    ///
354    /// ```
355    /// use willow_data_model::prelude::*;
356    /// use willow_data_model::prelude::*;
357    ///
358    /// let entry = Entry::builder()
359    ///     .namespace_id("family")
360    ///     .subspace_id("alfie")
361    ///     .path(Path::<4, 4, 4>::new())
362    ///     .timestamp(12345)
363    ///     .payload_digest("b")
364    ///     .payload_length(17)
365    ///     .build().unwrap();
366    ///
367    /// assert!(!entry.is_older_than(&entry));
368    ///
369    /// let lesser_timestamp = Entry::prefilled_builder(&entry).timestamp(5).build().unwrap();
370    /// assert!(!entry.is_older_than(&lesser_timestamp));
371    ///
372    /// let lesser_digest = Entry::prefilled_builder(&entry).payload_digest("a").build().unwrap();
373    /// assert!(!entry.is_older_than(&lesser_digest));
374    ///
375    /// let lesser_length = Entry::prefilled_builder(&entry).payload_length(0).build().unwrap();
376    /// assert!(!entry.is_older_than(&lesser_length));
377    ///
378    /// let greater_timestamp = Entry::prefilled_builder(&entry).timestamp(999999).build().unwrap();
379    /// assert!(entry.is_older_than(&greater_timestamp));
380    ///
381    /// let greater_digest = Entry::prefilled_builder(&entry).payload_digest("c").build().unwrap();
382    /// assert!(entry.is_older_than(&greater_digest));
383    ///
384    /// let greater_length = Entry::prefilled_builder(&entry).payload_length(99).build().unwrap();
385    /// assert!(entry.is_older_than(&greater_length));
386    /// ```
387    fn is_older_than<OtherEntry>(&self, other: &OtherEntry) -> bool
388    where
389        OtherEntry: Entrylike<MCL, MCC, MPL, N, S, PD>,
390        PD: Ord,
391    {
392        self.cmp_recency(other) == Ordering::Less
393    }
394
395    /// Returns whether this entry would [prefix prune](https://willowprotocol.org/specs/data-model/index.html#prefix_pruning) another entry.
396    ///
397    /// Prefix pruning powers deletion in Willow: whenever a data store would contain two entries, one of which pruens the other, the other is removed from the data store (or never inserted in the first place). Informally speaking, newer entries remove older entries, but only if they are in the same namespace and subspace, and only if the path of the newer entry is a prefix of the path of the older entry.
398    ///
399    /// More precisely: an entry `e1` prunes an entry `e2` if and only if
400    ///
401    /// - `e1.namespace_id() == e2.namespace_id()`,
402    /// - `e1.subspace_id() == e2.subspace_id()`,
403    /// - `e1.path().is_prefix_of(e2.path())`, and
404    /// - `e1.is_newer_than(&e2)`.
405    ///
406    /// This method is the reciprocal of [`EntrylikeExt::is_pruned_by`].
407    ///
408    /// # Examples
409    ///
410    ///
411    /// ```
412    /// use willow_data_model::prelude::*;
413    /// use willow_data_model::prelude::*;
414    ///
415    /// let entry = Entry::builder()
416    ///     .namespace_id("family")
417    ///     .subspace_id("alfie")
418    ///     .path(Path::<4, 4, 4>::from_slices(&[b"a", b"b"])?)
419    ///     .timestamp(12345)
420    ///     .payload_digest("b")
421    ///     .payload_length(17)
422    ///     .build().unwrap();
423    ///
424    /// let newer = Entry::prefilled_builder(&entry).timestamp(99999).build().unwrap();
425    /// assert!(!entry.prunes(&newer));
426    /// assert!(newer.prunes(&entry));
427    ///
428    /// let newer_and_prefix = Entry::prefilled_builder(&newer)
429    ///     .path(Path::<4, 4, 4>::from_slices(&[b"a"])?).build().unwrap();
430    /// assert!(!entry.prunes(&newer_and_prefix));
431    /// assert!(newer_and_prefix.prunes(&entry));
432    ///
433    /// let newer_and_extension = Entry::prefilled_builder(&newer)
434    ///     .path(Path::<4, 4, 4>::from_slices(&[b"a", b"b", b"c"])?).build().unwrap();
435    /// assert!(!entry.prunes(&newer_and_extension));
436    /// assert!(!newer_and_extension.prunes(&entry));
437    ///
438    /// let newer_but_unrelated_namespace = Entry::prefilled_builder(&newer)
439    ///     .namespace_id("bookclub").build().unwrap();
440    /// assert!(!entry.prunes(&newer_but_unrelated_namespace));
441    /// assert!(!newer_but_unrelated_namespace.prunes(&entry));
442    ///
443    /// let newer_but_unrelated_subspace = Entry::prefilled_builder(&newer)
444    ///     .subspace_id("betty").build().unwrap();
445    /// assert!(!entry.prunes(&newer_but_unrelated_subspace));
446    /// assert!(!newer_but_unrelated_subspace.prunes(&entry));
447    /// # Ok::<(), PathError>(())
448    /// ```
449    ///
450    fn prunes<OtherEntry>(&self, other: &OtherEntry) -> bool
451    where
452        OtherEntry: Entrylike<MCL, MCC, MPL, N, S, PD>,
453        N: PartialEq,
454        S: PartialEq,
455        PD: Ord,
456    {
457        self.is_newer_than(other)
458            && self.wdm_namespace_id() == other.wdm_namespace_id()
459            && self.wdm_subspace_id() == other.wdm_subspace_id()
460            && self.wdm_path().is_prefix_of(other.wdm_path())
461    }
462
463    /// Returns whether this entry would be [prefix pruned](https://willowprotocol.org/specs/data-model/index.html#prefix_pruning) by another entry.
464    ///
465    /// Prefix pruning powers deletion in Willow: whenever a data store would contain two entries, one of which pruens the other, the other is removed from the data store (or never inserted in the first place). Informally speaking, newer entries remove older entries, but only if they are in the same namespace and subspace, and only if the path of the newer entry is a prefix of the path of the older entry.
466    ///
467    /// More precisely: an entry `e1` prunes an entry `e2` if and only if
468    ///
469    /// - `e1.namespace_id() == e2.namespace_id()`,
470    /// - `e1.subspace_id() == e2.subspace_id()`,
471    /// - `e1.path().is_prefix_of(e2.path())`, and
472    /// - `e1.is_newer_than(&e2)`.
473    ///
474    /// This method is the reciprocal of [`EntrylikeExt::prunes`].
475    ///
476    /// # Examples
477    ///
478    ///
479    /// ```
480    /// use willow_data_model::prelude::*;
481    /// use willow_data_model::prelude::*;
482    ///
483    /// let entry = Entry::builder()
484    ///     .namespace_id("family")
485    ///     .subspace_id("alfie")
486    ///     .path(Path::<4, 4, 4>::from_slices(&[b"a", b"b"])?)
487    ///     .timestamp(12345)
488    ///     .payload_digest("b")
489    ///     .payload_length(17)
490    ///     .build().unwrap();
491    ///
492    /// let newer = Entry::prefilled_builder(&entry).timestamp(99999).build().unwrap();
493    /// assert!(entry.is_pruned_by(&newer));
494    /// assert!(!newer.is_pruned_by(&entry));
495    ///
496    /// let newer_and_prefix = Entry::prefilled_builder(&newer)
497    ///     .path(Path::<4, 4, 4>::from_slices(&[b"a"])?).build().unwrap();
498    /// assert!(entry.is_pruned_by(&newer_and_prefix));
499    /// assert!(!newer_and_prefix.is_pruned_by(&entry));
500    ///
501    /// let newer_and_extension = Entry::prefilled_builder(&newer)
502    ///     .path(Path::<4, 4, 4>::from_slices(&[b"a", b"b", b"c"])?).build().unwrap();
503    /// assert!(!entry.is_pruned_by(&newer_and_extension));
504    /// assert!(!newer_and_extension.is_pruned_by(&entry));
505    ///
506    /// let newer_but_unrelated_namespace = Entry::prefilled_builder(&newer)
507    ///     .namespace_id("bookclub").build().unwrap();
508    /// assert!(!entry.is_pruned_by(&newer_but_unrelated_namespace));
509    /// assert!(!newer_but_unrelated_namespace.is_pruned_by(&entry));
510    ///
511    /// let newer_but_unrelated_subspace = Entry::prefilled_builder(&newer)
512    ///     .subspace_id("betty").build().unwrap();
513    /// assert!(!entry.is_pruned_by(&newer_but_unrelated_subspace));
514    /// assert!(!newer_but_unrelated_subspace.is_pruned_by(&entry));
515    /// # Ok::<(), PathError>(())
516    /// ```
517    ///
518    fn is_pruned_by<OtherEntry>(&self, other: &OtherEntry) -> bool
519    where
520        OtherEntry: Entrylike<MCL, MCC, MPL, N, S, PD>,
521        N: PartialEq,
522        S: PartialEq,
523        PD: Ord,
524        Self: Sized,
525    {
526        other.prunes(self)
527    }
528}
529
530impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, E>
531    EntrylikeExt<MCL, MCC, MPL, N, S, PD> for E
532where
533    E: Entrylike<MCL, MCC, MPL, N, S, PD>,
534{
535}