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}