Skip to main content

content_addressable/
trait_def.rs

1//! The [`ContentAddressable`] trait — the one thing a type implements to gain
2//! a self-certifying identity.
3//!
4//! A type becomes content-addressable by defining a single method,
5//! [`canonical_form`](ContentAddressable::canonical_form), that produces its
6//! deterministic byte representation. Everything else — computing the
7//! [`ContentId`] and verifying against an expected id — is provided.
8
9use crate::content_id::ContentId;
10use crate::error::ContentError;
11
12/// A value that can name itself by its content.
13///
14/// Implementors provide [`canonical_form`](Self::canonical_form); the
15/// [`content_id`](Self::content_id) and [`verify`](Self::verify) methods are
16/// derived from it for free.
17///
18/// # Implementing
19///
20/// For any `#[derive(serde::Serialize)]` type, the canonical form is just its
21/// canonical dag-cbor encoding, so the implementation is one line:
22///
23/// ```
24/// use content_addressable::{canonical, ContentAddressable, ContentError};
25/// use serde::Serialize;
26///
27/// #[derive(Serialize)]
28/// struct Block {
29///     parent: Option<String>,
30///     payload: Vec<u8>,
31/// }
32///
33/// impl ContentAddressable for Block {
34///     fn canonical_form(&self) -> Result<Vec<u8>, ContentError> {
35///         canonical::to_canonical_dagcbor(self)
36///     }
37/// }
38///
39/// let block = Block { parent: None, payload: vec![1, 2, 3] };
40/// let id = block.content_id().unwrap();
41/// assert!(block.verify(&id).unwrap());
42/// ```
43pub trait ContentAddressable {
44    /// Produce the deterministic byte representation of this value.
45    ///
46    /// This is the *only* required method. The canonical bytes must be a
47    /// function of the value alone: equal values must produce equal bytes.
48    /// The recommended implementation defers to
49    /// [`to_canonical_dagcbor`](crate::canonical::to_canonical_dagcbor) on a
50    /// `Serialize` type, which guarantees that property.
51    ///
52    /// # Errors
53    ///
54    /// Returns [`ContentError`] if the value cannot be canonically encoded.
55    fn canonical_form(&self) -> Result<Vec<u8>, ContentError>;
56
57    /// Compute this value's [`ContentId`].
58    ///
59    /// Hashes the [`canonical_form`](Self::canonical_form) into a BLAKE3
60    /// CIDv1. Override only if you have a faster path that is provably
61    /// identical to the default.
62    ///
63    /// # Errors
64    ///
65    /// Propagates any error from [`canonical_form`](Self::canonical_form).
66    fn content_id(&self) -> Result<ContentId, ContentError> {
67        Ok(ContentId::from_canonical_bytes(&self.canonical_form()?))
68    }
69
70    /// Check whether this value's content id matches an `expected` id.
71    ///
72    /// Returns `Ok(true)` if the recomputed id equals `expected`, `Ok(false)`
73    /// otherwise. This is the integrity check at the heart of the doctrine:
74    /// the value re-derives its own identity and compares it to the claim.
75    ///
76    /// # Mismatch is `Ok(false)`, not an error (FROZEN at 0.1.0)
77    ///
78    /// A *mismatch* is **not** an error — it returns `Ok(false)`. A negative
79    /// answer to "do these match?" is a successful, expected result, not a
80    /// failure to check; forcing it through the `Err` channel would conflate "I
81    /// checked, the answer is no" with "I couldn't check". The `?`-friendly
82    /// `Result<bool>` also composes cleanly in boolean logic
83    /// (`if a.verify(&id1)? && b.verify(&id2)? { … }`). This return contract is a
84    /// **frozen** part of the `0.1.0` API surface (README gate item #8) —
85    /// flipping `Ok(false)` to an `Err` arm later would be a breaking change.
86    ///
87    /// If you want a mismatch to short-circuit via `?`, use the strict helper
88    /// [`ensure_content_id`](Self::ensure_content_id), which returns
89    /// `Err(`[`ContentError::VerificationFailed`]`)` on mismatch — don't hand-roll
90    /// it.
91    ///
92    /// # Errors
93    ///
94    /// Propagates any error from [`content_id`](Self::content_id) only (i.e. an
95    /// encoding failure in `canonical_form`); never an `Err` on a clean
96    /// mismatch.
97    fn verify(&self, expected: &ContentId) -> Result<bool, ContentError> {
98        Ok(&self.content_id()? == expected)
99    }
100
101    /// Verify, returning an **error on mismatch** instead of `Ok(false)`.
102    ///
103    /// Like [`verify`](Self::verify), but a mismatch is reported as
104    /// `Err(`[`ContentError::VerificationFailed`]`)` carrying both ids (as their
105    /// [`Display`](core::fmt::Display) strings, multibase base32-lower `b…`),
106    /// rather than `Ok(false)`. On a match it returns `Ok(())`. Use this when a
107    /// mismatch should short-circuit through `?`; use [`verify`](Self::verify)
108    /// when you want the boolean to compose in further logic.
109    ///
110    /// This is the strict form the crate's doctrine promises: it makes
111    /// [`ContentError::VerificationFailed`] a real, reachable, tested error path
112    /// rather than a name callers must construct by hand. It is a defaulted trait
113    /// method, so every implementor gets it for free. Both `verify` and this
114    /// helper are **frozen** for the `0.1.0` API surface (README gate item #8).
115    ///
116    /// # Errors
117    ///
118    /// - [`ContentError::VerificationFailed`] if the recomputed id differs from
119    ///   `expected`.
120    /// - Otherwise propagates any error from [`content_id`](Self::content_id)
121    ///   (e.g. an encoding failure in `canonical_form`).
122    fn ensure_content_id(&self, expected: &ContentId) -> Result<(), ContentError> {
123        let computed = self.content_id()?;
124        if &computed == expected {
125            Ok(())
126        } else {
127            Err(ContentError::VerificationFailed {
128                expected: expected.to_string(),
129                computed: computed.to_string(),
130            })
131        }
132    }
133}