wnfs_common/storable.rs
1//! Defines the [`Storable`] trait, which defines the `.load` and `.store` functions
2//! that are implemented for most WNFS structures, such as `PublicFile`, `PublicDirectory`,
3//! `PublicNode`, `HamtForest` etc.
4use crate::{
5 BlockStore, CODEC_DAG_CBOR,
6 utils::{Arc, CondSend, CondSync},
7};
8use anyhow::{Result, bail};
9use async_once_cell::OnceCell;
10use bytes::Bytes;
11use cid::Cid;
12use futures::Future;
13use serde::{Serialize, de::DeserializeOwned};
14
15//--------------------------------------------------------------------------------------------------
16// Macros
17//--------------------------------------------------------------------------------------------------
18
19#[macro_export]
20macro_rules! impl_storable_from_serde {
21 ( $( $ty:ty $( : < $( $generics:ident ),+ > )? ),+ ) => {
22 $(
23 impl $( < $( $generics ),+ > )? $crate::Storable for $ty $( where $( $generics: ::serde::Serialize + ::serde::de::DeserializeOwned + Clone + $crate::utils::CondSync ),+ )?{
24 type Serializable = $ty;
25
26 async fn to_serializable(&self, _store: &impl $crate::BlockStore) -> ::anyhow::Result<Self::Serializable> {
27 Ok(self.clone())
28 }
29
30 async fn from_serializable(_cid: Option<&$crate::Cid>, serializable: Self::Serializable) -> ::anyhow::Result<Self> {
31 Ok(serializable)
32 }
33 }
34 )+
35 };
36}
37
38//--------------------------------------------------------------------------------------------------
39// Type Definitions
40//--------------------------------------------------------------------------------------------------
41
42/// The trait that defines how to store something in a blockstore.
43///
44/// This works via a two-tiered system, where the actual in-memory representation
45/// (the struct that implements this trait) is not the same as the at-rest
46/// representation of itself.
47/// The at-rest representation is given by the `Serializable` associated type.
48///
49/// Commonly, the `Serializable` type implements serde's `Serialize` and `Deserialize`
50/// traits and thus can automatically be used without having to implement `StoreIpld`
51/// and `LoadIpld` yourself. In that case, the default implementation will use
52/// `serde_ipld_dagcbor`.
53///
54/// This trait also optionally supports memoizing serialization via the `persisted_as` function.
55/// You can add a field `persisted_as: OnceCell<Cid>` to your in-memory representation and
56/// return it in the `persisted_as` function and any `store` calls will automatically populate
57/// that cache.
58/// If you do so, remember to initialize the `OnceCell` if a `Cid` is passed in the
59/// `from_serializable` call, such that a `store` call right after a `load` call is practically
60/// free.
61pub trait Storable: Sized {
62 /// The at-rest representation of this storable type.
63 type Serializable: StoreIpld + LoadIpld + CondSync;
64
65 /// Turn the current type into the at-rest representation of this type.
66 fn to_serializable(
67 &self,
68 store: &impl BlockStore,
69 ) -> impl Future<Output = Result<Self::Serializable>> + CondSend;
70
71 /// Take an at-rest representation of this type and turn it into the in-memory representation.
72 /// You can use the `cid` parameter to populate a cache.
73 fn from_serializable(
74 cid: Option<&Cid>,
75 serializable: Self::Serializable,
76 ) -> impl Future<Output = Result<Self>> + CondSend;
77
78 /// Return a serialization cache, if it exists.
79 /// By default, this always returns `None`.
80 fn persisted_as(&self) -> Option<&OnceCell<Cid>> {
81 None
82 }
83
84 /// Store this data type in a given `BlockStore`.
85 ///
86 /// This will short-circuit by using the `persisted_as` once-cell, if available.
87 fn store(&self, store: &impl BlockStore) -> impl Future<Output = Result<Cid>> + CondSend
88 where
89 Self: CondSync,
90 {
91 let store_future = async {
92 let (bytes, codec) = self.to_serializable(store).await?.encode_ipld()?;
93 Ok(store.put_block(bytes, codec).await?)
94 };
95
96 async {
97 if let Some(persisted_as) = self.persisted_as() {
98 persisted_as.get_or_try_init(store_future).await.cloned()
99 } else {
100 store_future.await
101 }
102 }
103 }
104
105 /// Try to load a value of this type from a CID.
106 ///
107 /// This will pass on the CID to the `from_serializable` function so it can
108 /// populate a cache in some cases.
109 fn load(cid: &Cid, store: &impl BlockStore) -> impl Future<Output = Result<Self>> + CondSend {
110 async {
111 let bytes = store.get_block(cid).await?;
112 let serializable = Self::Serializable::decode_ipld(cid, bytes)?;
113 Self::from_serializable(Some(cid), serializable).await
114 }
115 }
116}
117
118pub trait StoreIpld {
119 fn encode_ipld(&self) -> Result<(Bytes, u64)>;
120}
121
122pub trait LoadIpld: Sized {
123 fn decode_ipld(cid: &Cid, bytes: Bytes) -> Result<Self>;
124}
125
126impl<T: Serialize> StoreIpld for T {
127 fn encode_ipld(&self) -> Result<(Bytes, u64)> {
128 let bytes = serde_ipld_dagcbor::to_vec(self)?;
129 Ok((bytes.into(), CODEC_DAG_CBOR))
130 }
131}
132
133impl<T: DeserializeOwned + Sized> LoadIpld for T {
134 fn decode_ipld(cid: &Cid, bytes: Bytes) -> Result<Self> {
135 let codec = cid.codec();
136 let dag_cbor: u64 = CODEC_DAG_CBOR;
137 if codec != dag_cbor {
138 bail!("Expected dag-cbor codec, but got {codec:X} in CID {cid}");
139 }
140 Ok(serde_ipld_dagcbor::from_slice(bytes.as_ref())?)
141 }
142}
143
144//--------------------------------------------------------------------------------------------------
145// Implementations
146//--------------------------------------------------------------------------------------------------
147
148// We need to choose *one* blanket implementation, and unfortunately
149// you can't `impl Storable for Arc<MyType>` outside of this module,
150// because that'd be an orphan instance. So instead we're providing a
151// macro and implement the `Arc<T>` instance generically here.
152
153// #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
154// #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
155// impl<T: StoreIpld + LoadIpld + CondSync + Clone> Storable for T {
156// type Serializable = T;
157
158// async fn to_serializable(&self, _store: &impl BlockStore) -> Result<Self::Serializable> {
159// Ok(self.clone())
160// }
161
162// async fn from_serializable(serializable: Self::Serializable) -> Result<Self> {
163// Ok(serializable)
164// }
165// }
166
167impl<T: Storable + CondSync> Storable for Arc<T> {
168 type Serializable = T::Serializable;
169
170 async fn to_serializable(&self, store: &impl BlockStore) -> Result<Self::Serializable> {
171 self.as_ref().to_serializable(store).await
172 }
173
174 async fn from_serializable(
175 cid: Option<&Cid>,
176 serializable: Self::Serializable,
177 ) -> Result<Self> {
178 Ok(Arc::new(T::from_serializable(cid, serializable).await?))
179 }
180
181 fn persisted_as(&self) -> Option<&OnceCell<Cid>> {
182 self.as_ref().persisted_as()
183 }
184}
185
186impl_storable_from_serde! { [u8; 0], [u8; 1], [u8; 2], [u8; 4], [u8; 8], [u8; 16], [u8; 32] }
187impl_storable_from_serde! { usize, u128, u64, u32, u16, u8, isize, i128, i64, i32, i16, i8 }
188impl_storable_from_serde! { String }
189impl_storable_from_serde! {
190 (A,): <A>,
191 (A, B): <A, B>,
192 (A, B, C): <A, B, C>,
193 (A, B, C, D): <A, B, C, D>,
194 (A, B, C, D, E): <A, B, C, D, E>,
195 (A, B, C, D, E, F): <A, B, C, D, E, F>,
196 (A, B, C, D, E, F, G): <A, B, C, D, E, F, G>,
197 (A, B, C, D, E, F, G, H): <A, B, C, D, E, F, G, H>,
198 (A, B, C, D, E, F, G, H, I): <A, B, C, D, E, F, G, H, I>,
199 (A, B, C, D, E, F, G, H, I, J): <A, B, C, D, E, F, G, H, I, J>,
200 (A, B, C, D, E, F, G, H, I, J, K): <A, B, C, D, E, F, G, H, I, J, K>
201}