Skip to main content

heddle_object_model/object/collaboration/
canonical_body.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use std::sync::Mutex;
4
5/// Cached canonical MessagePack body for a content-addressed value.
6///
7/// Not part of the serialized form. Equality ignores it. [`Clone`] drops it so a
8/// clone cannot keep bytes that no longer match mutated fields. `encode` and
9/// `decode` store the body; `id` / `hash` reuse it instead of serializing again.
10/// Struct literals set this to [`Default::default`].
11#[derive(Debug, Default)]
12pub struct CanonicalBody {
13    bytes: Mutex<Option<Vec<u8>>>,
14}
15
16impl Clone for CanonicalBody {
17    fn clone(&self) -> Self {
18        Self::default()
19    }
20}
21
22impl PartialEq for CanonicalBody {
23    fn eq(&self, _other: &Self) -> bool {
24        true
25    }
26}
27
28impl Eq for CanonicalBody {}
29
30impl CanonicalBody {
31    pub(crate) fn cloned(&self) -> Option<Vec<u8>> {
32        self.lock().clone()
33    }
34
35    pub(crate) fn store(&self, bytes: Vec<u8>) {
36        *self.lock() = Some(bytes);
37    }
38
39    pub(crate) fn clear(&self) {
40        *self.lock() = None;
41    }
42
43    /// Debug builds require `fresh` to reproduce `cached`. Release builds trust
44    /// the body stored by the last successful `encode` or `decode`.
45    pub(crate) fn debug_matches<E: std::fmt::Display>(
46        cached: &[u8],
47        fresh: impl FnOnce() -> Result<Vec<u8>, E>,
48    ) {
49        #[cfg(debug_assertions)]
50        match fresh() {
51            Ok(bytes) => debug_assert_eq!(
52                cached,
53                bytes.as_slice(),
54                "cached canonical body does not match fields"
55            ),
56            Err(error) => {
57                panic!("cached canonical body outlives a value that no longer encodes: {error}")
58            }
59        }
60        #[cfg(not(debug_assertions))]
61        {
62            let _ = (cached, fresh);
63        }
64    }
65
66    fn lock(&self) -> std::sync::MutexGuard<'_, Option<Vec<u8>>> {
67        self.bytes.lock().unwrap_or_else(|err| err.into_inner())
68    }
69}