ipld_block_builder/
builder.rs

1use crate::batch::Batch;
2use crate::codec::{Decoder, Encoder, Encrypted, IpldDecoder};
3use crate::path::DagPath;
4use libipld::cid::Cid;
5use libipld::codec::{Decode, Encode};
6use libipld::error::Result;
7use libipld::ipld::Ipld;
8use libipld::store::{AliasStore, MultiUserStore, ReadonlyStore, Store, Visibility};
9use std::path::Path;
10
11/// Generic block builder for creating blocks.
12pub struct BlockBuilder<S, C> {
13    store: S,
14    codec: C,
15    visibility: Visibility,
16}
17
18impl<S, C> BlockBuilder<S, C> {
19    /// Creates a builder for public blocks.
20    pub fn new(store: S, codec: C) -> Self {
21        Self {
22            store,
23            codec,
24            visibility: Visibility::Public,
25        }
26    }
27
28    /// Gets the visibility of the builder.
29    pub fn visibility(&self) -> Visibility {
30        self.visibility
31    }
32
33    /// Gets the store of the builder.
34    pub fn store(&self) -> &S {
35        &self.store
36    }
37
38    /// Gets the codec of the builder.
39    pub fn codec(&self) -> &C {
40        &self.codec
41    }
42}
43
44impl<S, C: Encrypted> BlockBuilder<S, C> {
45    /// Creates a builder for private blocks.
46    pub fn new_private(store: S, codec: C) -> Self {
47        Self {
48            store,
49            codec,
50            visibility: Visibility::Private,
51        }
52    }
53}
54
55impl<S: ReadonlyStore, C: Decoder> BlockBuilder<S, C> {
56    /// Returns the decoded block with cid.
57    pub async fn get<D: Decode<C::Codec>>(&self, cid: &Cid) -> Result<D> {
58        let data = self.store.get(cid).await?;
59        self.codec.decode(cid, &data)
60    }
61}
62
63impl<S: ReadonlyStore, C: IpldDecoder> BlockBuilder<S, C> {
64    /// Returns the ipld representation of a block with cid.
65    pub async fn get_ipld(&self, cid: &Cid) -> Result<Ipld> {
66        let data = self.store.get(cid).await?;
67        self.codec.decode_ipld(cid, &data)
68    }
69
70    /// Resolves a path recursively and returns the ipld.
71    pub async fn get_path(&self, path: &DagPath<'_>) -> Result<Ipld> {
72        let mut root = self.get_ipld(path.root()).await?;
73        let mut ipld = &root;
74        for segment in path.path().iter() {
75            ipld = ipld.get(segment)?;
76            if let Ipld::Link(cid) = ipld {
77                root = self.get_ipld(cid).await?;
78                ipld = &root;
79            }
80        }
81        Ok(ipld.clone())
82    }
83}
84
85impl<S: Store, C: Encoder + Clone> BlockBuilder<S, C> {
86    /// Creates a new batch.
87    pub fn create_batch(&self) -> Batch<C> {
88        Batch::new(self.codec.clone())
89    }
90
91    /// Creates a new batch with capacity.
92    pub fn create_batch_with_capacity(&self, capacity: usize) -> Batch<C> {
93        Batch::with_capacity(self.codec.clone(), capacity)
94    }
95
96    /// Encodes and inserts a block into the store.
97    pub async fn insert<E: Encode<C::Codec>>(&self, e: &E) -> Result<Cid> {
98        let mut batch = self.create_batch();
99        batch.insert(e)?;
100        self.insert_batch(batch).await
101    }
102
103    /// Inserts a batch of blocks atomically pinning the last one.
104    pub async fn insert_batch<T>(&self, batch: Batch<T>) -> Result<Cid> {
105        Ok(self
106            .store
107            .insert_batch(batch.into_vec(), self.visibility)
108            .await?)
109    }
110}
111
112impl<S: Store, C> BlockBuilder<S, C> {
113    /// Flushes the store to disk.
114    pub async fn flush(&self) -> Result<()> {
115        Ok(self.store.flush().await?)
116    }
117
118    /// Unpins a block from the store marking it ready for garbage collection.
119    pub async fn unpin(&self, cid: &Cid) -> Result<()> {
120        Ok(self.store.unpin(cid).await?)
121    }
122}
123
124impl<S: MultiUserStore, C> BlockBuilder<S, C> {
125    /// Pins a block in the store.
126    pub async fn pin(&self, cid: &Cid, path: &Path) -> Result<()> {
127        Ok(self.store.pin(cid, path).await?)
128    }
129}
130
131impl<S: AliasStore, C> BlockBuilder<S, C> {
132    /// Creates an alias for a cid.
133    pub async fn alias(&self, alias: &[u8], cid: &Cid) -> Result<()> {
134        Ok(self.store.alias(alias, cid, self.visibility).await?)
135    }
136
137    /// Removes an alias.
138    pub async fn unalias(&self, alias: &[u8]) -> Result<()> {
139        Ok(self.store.unalias(alias).await?)
140    }
141
142    /// Resolves an alias.
143    pub async fn resolve(&self, alias: &[u8]) -> Result<Option<Cid>> {
144        Ok(self.store.resolve(alias).await?)
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151    #[cfg(feature = "crypto")]
152    use crate::crypto::Key;
153    use crate::Codec;
154    #[cfg(feature = "crypto")]
155    use crate::StrobeCodec;
156    use libipld::mem::MemStore;
157    use libipld::{ipld, DagCbor};
158
159    #[async_std::test]
160    async fn test_block_builder() {
161        let store = MemStore::default();
162        let codec = Codec::new();
163        let builder = BlockBuilder::new(store, codec);
164
165        let block1 = ipld!({
166            "value": 42,
167        });
168        let cid1 = builder.insert(&block1).await.unwrap();
169        let block1_2: Ipld = builder.get(&cid1).await.unwrap();
170        assert_eq!(block1, block1_2);
171
172        let block2 = ipld!({
173            "name": cid1,
174        });
175        let cid2 = builder.insert(&block2).await.unwrap();
176        let block2_2: Ipld = builder.get(&cid2).await.unwrap();
177        assert_eq!(block2, block2_2);
178    }
179
180    #[async_std::test]
181    async fn test_dag() {
182        let store = MemStore::default();
183        let codec = Codec::new();
184        let builder = BlockBuilder::new(store, codec);
185        let ipld1 = ipld!({"a": 3});
186        let cid = builder.insert(&ipld1).await.unwrap();
187        let ipld2 = ipld!({"root": [{"child": &cid}]});
188        let root = builder.insert(&ipld2).await.unwrap();
189        let path = DagPath::new(&root, "root/0/child/a");
190        assert_eq!(builder.get_path(&path).await.unwrap(), Ipld::Integer(3));
191    }
192
193    #[derive(Clone, DagCbor, Debug, Eq, PartialEq)]
194    struct Identity {
195        id: u64,
196        name: String,
197        age: u8,
198    }
199
200    #[async_std::test]
201    #[cfg(feature = "crypto")]
202    async fn test_block_builder_private() {
203        let key = Key::from(b"private encryption key".to_vec());
204        let store = MemStore::default();
205        let codec = StrobeCodec::new(key);
206        let builder = BlockBuilder::new_private(store, codec);
207
208        let identity = Identity {
209            id: 0,
210            name: "David Craven".into(),
211            age: 26,
212        };
213        let cid = builder.insert(&identity).await.unwrap();
214        let identity2 = builder.get(&cid).await.unwrap();
215        assert_eq!(identity, identity2);
216    }
217
218    #[async_std::test]
219    #[cfg(feature = "crypto")]
220    async fn test_dag_private() {
221        let key = Key::from(b"private encryption key".to_vec());
222        let store = MemStore::default();
223        let codec = StrobeCodec::new(key);
224        let builder = BlockBuilder::new_private(store, codec);
225        let ipld1 = ipld!({"a": 3});
226        let cid = builder.insert(&ipld1).await.unwrap();
227        let ipld2 = ipld!({"root": [{"child": &cid}]});
228        let root = builder.insert(&ipld2).await.unwrap();
229        let path = DagPath::new(&root, "root/0/child/a");
230        assert_eq!(builder.get_path(&path).await.unwrap(), Ipld::Integer(3));
231    }
232}