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}