flmodules/flo/
blob.rs

1use std::collections::HashMap;
2
3use bytes::Bytes;
4use flarch::nodeids::U256;
5use flcrypto::{access::Condition, signer::Signer};
6use flmacro::AsU256;
7use serde::{Deserialize, Serialize};
8
9use crate::dht_storage::core::Cuckoo;
10
11use super::{flo::FloWrapper, realm::RealmID};
12
13#[derive(AsU256, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
14pub struct BlobID(U256);
15
16pub type FloBlob = FloWrapper<Blob>;
17
18#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
19pub struct Blob {
20    blob_type: String,
21    links: HashMap<String, Vec<BlobID>>,
22    values: HashMap<String, String>,
23    datas: HashMap<String, Bytes>,
24}
25
26#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
27pub struct BlobPage(pub Blob);
28
29pub type FloBlobPage = FloWrapper<BlobPage>;
30
31#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
32pub struct BlobTag(pub Blob);
33
34pub type FloBlobTag = FloWrapper<BlobTag>;
35
36impl FloBlobPage {
37    pub fn new(
38        realm: RealmID,
39        cond: Condition,
40        path: &str,
41        index: Bytes,
42        parent: Option<BlobID>,
43        signers: &[&Signer],
44    ) -> anyhow::Result<Self> {
45        Self::new_cuckoo(realm, cond, path, index, parent, Cuckoo::None, signers)
46    }
47
48    pub fn new_cuckoo(
49        realm: RealmID,
50        cond: Condition,
51        path: &str,
52        index: Bytes,
53        parent: Option<BlobID>,
54        cuckoo: Cuckoo,
55        signers: &[&Signer],
56    ) -> anyhow::Result<Self> {
57        let links = parent
58            .map(|p| [("parents".to_string(), vec![p])].into_iter().collect())
59            .unwrap_or(HashMap::new());
60        Self::from_type_cuckoo(
61            realm,
62            cond,
63            cuckoo,
64            BlobPage(Blob {
65                blob_type: "re.fledg.page".into(),
66                links,
67                datas: [("index.html".to_string(), index)].into(),
68                values: [("path".to_string(), path.into())].into(),
69            }),
70            signers,
71        )
72    }
73
74    pub fn get_index(&self) -> String {
75        self.get_data("index.html")
76            .map(|b| String::from_utf8(b.to_vec()).unwrap_or_default())
77            .unwrap_or("".into())
78    }
79
80    pub fn blob_id(&self) -> BlobID {
81        (*self.flo_id()).into()
82    }
83}
84
85impl std::fmt::Display for FloBlobPage {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        f.write_fmt(format_args!("  {}\n", self.flo_id()))?;
88        f.write_fmt(format_args!(
89            "    {}\n",
90            self.get_path()
91                .map(|p| format!("path: {p}"))
92                .unwrap_or("no path".into())
93        ))?;
94        f.write_fmt(format_args!(
95            "    parents: {}\n",
96            self.get_parents()
97                .iter()
98                .map(|p| format!("{p}"))
99                .collect::<Vec<_>>()
100                .join(", ")
101        ))?;
102        f.write_fmt(format_args!("    version: {}\n", self.version()))?;
103        f.write_fmt(format_args!(
104            "    children: {}\n",
105            self.get_children()
106                .iter()
107                .map(|p| format!("{p}"))
108                .collect::<Vec<_>>()
109                .join(", ")
110        ))?;
111        f.write_fmt(format_args!(
112            "    files:\n{}",
113            self.datas()
114                .iter()
115                .map(|(k, v)| format!("      {k} -> {} B", v.len()))
116                .collect::<Vec<_>>()
117                .join("\n")
118        ))
119    }
120}
121
122impl BlobAccess for FloBlobPage {
123    fn get_blob(&self) -> &Blob {
124        &self.cache().0
125    }
126
127    fn get_blob_mut(&mut self) -> &mut Blob {
128        &mut self.cache_mut().0
129    }
130}
131
132impl BlobFamily for FloBlobPage {}
133impl BlobPath for FloBlobPage {}
134
135impl FloBlobTag {
136    pub fn new(
137        realm: RealmID,
138        cond: Condition,
139        name: &str,
140        parent: Option<BlobID>,
141        signers: &[&Signer],
142    ) -> anyhow::Result<Self> {
143        Self::new_cuckoo(realm, cond, name, parent, Cuckoo::None, signers)
144    }
145
146    pub fn new_cuckoo(
147        realm: RealmID,
148        cond: Condition,
149        name: &str,
150        parent: Option<BlobID>,
151        cuckoo: Cuckoo,
152        signers: &[&Signer],
153    ) -> anyhow::Result<Self> {
154        let links = parent
155            .map(|p| [("parents".to_string(), vec![p])].into_iter().collect())
156            .unwrap_or(HashMap::new());
157        Self::from_type_cuckoo(
158            realm,
159            cond,
160            cuckoo,
161            BlobTag(Blob {
162                blob_type: "re.fledg.tag".into(),
163                links,
164                datas: HashMap::new(),
165                values: [("name".to_string(), name.into())].into(),
166            }),
167            signers,
168        )
169    }
170
171    pub fn blob_id(&self) -> BlobID {
172        (*self.flo_id()).into()
173    }
174}
175
176impl BlobAccess for FloBlobTag {
177    fn get_blob(&self) -> &Blob {
178        &self.cache().0
179    }
180
181    fn get_blob_mut(&mut self) -> &mut Blob {
182        &mut self.cache_mut().0
183    }
184}
185
186impl BlobFamily for FloBlobTag {}
187impl BlobPath for FloBlobTag {}
188
189impl Blob {
190    pub fn new(blob_type: &str) -> Self {
191        Self {
192            blob_type: blob_type.to_string(),
193            links: HashMap::new(),
194            values: HashMap::new(),
195            datas: HashMap::new(),
196        }
197    }
198}
199
200impl BlobFamily for FloBlob {}
201impl BlobPath for FloBlob {}
202
203impl BlobAccess for FloBlob {
204    fn get_blob(&self) -> &Blob {
205        self.cache()
206    }
207
208    fn get_blob_mut(&mut self) -> &mut Blob {
209        self.cache_mut()
210    }
211}
212
213pub trait BlobAccess {
214    fn get_blob(&self) -> &Blob;
215    fn get_blob_mut(&mut self) -> &mut Blob;
216
217    fn links(&self) -> &HashMap<String, Vec<BlobID>> {
218        &self.get_blob().links
219    }
220
221    fn values(&self) -> &HashMap<String, String> {
222        &self.get_blob().values
223    }
224
225    fn datas(&self) -> &HashMap<String, Bytes> {
226        &self.get_blob().datas
227    }
228
229    fn get_data(&self, key: &str) -> Option<&Bytes> {
230        self.datas().get(key)
231    }
232
233    fn set_data(&mut self, key: String, value: Bytes) {
234        self.datas_mut().insert(key, value);
235    }
236
237    fn links_mut(&mut self) -> &mut HashMap<String, Vec<BlobID>> {
238        &mut self.get_blob_mut().links
239    }
240
241    fn values_mut(&mut self) -> &mut HashMap<String, String> {
242        &mut self.get_blob_mut().values
243    }
244
245    fn datas_mut(&mut self) -> &mut HashMap<String, Bytes> {
246        &mut self.get_blob_mut().datas
247    }
248}
249
250pub trait BlobFamily: BlobAccess {
251    fn set_parents(&mut self, parents: Vec<BlobID>) {
252        self.links_mut().insert("parents".into(), parents);
253    }
254    fn add_parent(&mut self, parent: BlobID) {
255        self.links_mut()
256            .entry("parents".into())
257            .or_insert_with(Vec::new)
258            .push(parent);
259    }
260    fn rm_parent(&mut self, parent: &BlobID) {
261        self.links_mut()
262            .get_mut("parents")
263            .map(|parents| parents.retain(|p| p != parent));
264    }
265    fn get_parents(&self) -> Vec<BlobID> {
266        self.links().get("parents").unwrap_or(&vec![]).clone()
267    }
268    fn set_children(&mut self, children: Vec<BlobID>) {
269        self.links_mut().insert("children".into(), children);
270    }
271    fn add_child(&mut self, child: BlobID) {
272        self.links_mut()
273            .entry("children".into())
274            .or_insert_with(Vec::new)
275            .push(child);
276    }
277    fn rm_child(&mut self, child: &BlobID) {
278        self.links_mut()
279            .get_mut("children")
280            .map(|children| children.retain(|c| c != child));
281    }
282    fn get_children(&self) -> Vec<BlobID> {
283        self.links().get("children").unwrap_or(&vec![]).clone()
284    }
285}
286
287pub trait BlobPath: BlobAccess {
288    fn set_path(&mut self, path: String) {
289        self.values_mut().insert("path".into(), path);
290    }
291    fn get_path(&self) -> Option<&String> {
292        self.values().get("path")
293    }
294}
295
296impl BlobAccess for Blob {
297    fn get_blob(&self) -> &Blob {
298        &self
299    }
300
301    fn get_blob_mut(&mut self) -> &mut Blob {
302        self
303    }
304}
305
306impl BlobAccess for BlobPage {
307    fn get_blob(&self) -> &Blob {
308        &self.0
309    }
310
311    fn get_blob_mut(&mut self) -> &mut Blob {
312        &mut self.0
313    }
314}
315
316impl BlobAccess for BlobTag {
317    fn get_blob(&self) -> &Blob {
318        &self.0
319    }
320
321    fn get_blob_mut(&mut self) -> &mut Blob {
322        &mut self.0
323    }
324}
325
326impl BlobPath for Blob {}
327impl BlobFamily for Blob {}
328
329impl BlobPath for BlobPage {}
330impl BlobFamily for BlobPage {}
331
332impl BlobPath for BlobTag {}
333impl BlobFamily for BlobTag {}
334
335pub trait BlobPathFamily: BlobPath + BlobFamily {}
336
337impl<T: BlobPath + BlobFamily> BlobPathFamily for T {}
338
339#[cfg(test)]
340mod test {
341    use flcrypto::access::Condition;
342
343    use crate::{flo::crypto::FloBadge, testing::wallet::Wallet};
344
345    use super::*;
346
347    #[test]
348    fn test_update() -> anyhow::Result<()> {
349        let mut wallet = Wallet::new();
350        FloBadge::from_type(RealmID::rnd(), Condition::Fail, wallet.get_badge(), &[])?;
351        let flb = FloBlobPage::new(
352            RealmID::rnd(),
353            Condition::Verifier(wallet.get_verifier()),
354            "",
355            Bytes::from(""),
356            None,
357            &[&wallet.get_signer()],
358        )?;
359
360        let flb2 = flb.edit_data_signers(
361            Condition::Verifier(wallet.get_verifier()),
362            |bp| bp.set_parents(vec![BlobID::rnd()]),
363            &[&mut wallet.get_signer()],
364        )?;
365
366        assert_eq!(1, flb2.version());
367
368        let flb3 = flb2.edit_data_signers(
369            Condition::Verifier(wallet.get_verifier()),
370            |bp| {
371                bp.0.datas
372                    .insert("index.html".into(), "<html><h1>Root</h1></html>".into());
373            },
374            &[&mut wallet.get_signer()],
375        )?;
376
377        assert_eq!(2, flb3.version());
378
379        Ok(())
380    }
381}