use std::collections::HashMap;
use bytes::Bytes;
use flarch::nodeids::U256;
use flcrypto::{access::Condition, signer::Signer};
use flmacro::AsU256;
use serde::{Deserialize, Serialize};
use crate::dht_storage::core::Cuckoo;
use super::{flo::FloWrapper, realm::RealmID};
#[derive(AsU256, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
pub struct BlobID(U256);
pub type FloBlob = FloWrapper<Blob>;
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Blob {
blob_type: String,
links: HashMap<String, Vec<BlobID>>,
values: HashMap<String, String>,
datas: HashMap<String, Bytes>,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct BlobPage(pub Blob);
pub type FloBlobPage = FloWrapper<BlobPage>;
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct BlobTag(pub Blob);
pub type FloBlobTag = FloWrapper<BlobTag>;
impl FloBlobPage {
pub fn new(
realm: RealmID,
cond: Condition,
path: &str,
index: Bytes,
parent: Option<BlobID>,
signers: &[&Signer],
) -> anyhow::Result<Self> {
Self::new_cuckoo(realm, cond, path, index, parent, Cuckoo::None, signers)
}
pub fn new_cuckoo(
realm: RealmID,
cond: Condition,
path: &str,
index: Bytes,
parent: Option<BlobID>,
cuckoo: Cuckoo,
signers: &[&Signer],
) -> anyhow::Result<Self> {
let links = parent
.map(|p| [("parents".to_string(), vec![p])].into_iter().collect())
.unwrap_or(HashMap::new());
Self::from_type_cuckoo(
realm,
cond,
cuckoo,
BlobPage(Blob {
blob_type: "re.fledg.page".into(),
links,
datas: [("index.html".to_string(), index)].into(),
values: [("path".to_string(), path.into())].into(),
}),
signers,
)
}
pub fn get_index(&self) -> String {
self.get_data("index.html")
.map(|b| String::from_utf8(b.to_vec()).unwrap_or_default())
.unwrap_or("".into())
}
pub fn blob_id(&self) -> BlobID {
(*self.flo_id()).into()
}
}
impl std::fmt::Display for FloBlobPage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(" {}\n", self.flo_id()))?;
f.write_fmt(format_args!(
" {}\n",
self.get_path()
.map(|p| format!("path: {p}"))
.unwrap_or("no path".into())
))?;
f.write_fmt(format_args!(
" parents: {}\n",
self.get_parents()
.iter()
.map(|p| format!("{p}"))
.collect::<Vec<_>>()
.join(", ")
))?;
f.write_fmt(format_args!(" version: {}\n", self.version()))?;
f.write_fmt(format_args!(
" children: {}\n",
self.get_children()
.iter()
.map(|p| format!("{p}"))
.collect::<Vec<_>>()
.join(", ")
))?;
f.write_fmt(format_args!(
" files:\n{}",
self.datas()
.iter()
.map(|(k, v)| format!(" {k} -> {} B", v.len()))
.collect::<Vec<_>>()
.join("\n")
))
}
}
impl BlobAccess for FloBlobPage {
fn get_blob(&self) -> &Blob {
&self.cache().0
}
fn get_blob_mut(&mut self) -> &mut Blob {
&mut self.cache_mut().0
}
}
impl BlobFamily for FloBlobPage {}
impl BlobPath for FloBlobPage {}
impl FloBlobTag {
pub fn new(
realm: RealmID,
cond: Condition,
name: &str,
parent: Option<BlobID>,
signers: &[&Signer],
) -> anyhow::Result<Self> {
Self::new_cuckoo(realm, cond, name, parent, Cuckoo::None, signers)
}
pub fn new_cuckoo(
realm: RealmID,
cond: Condition,
name: &str,
parent: Option<BlobID>,
cuckoo: Cuckoo,
signers: &[&Signer],
) -> anyhow::Result<Self> {
let links = parent
.map(|p| [("parents".to_string(), vec![p])].into_iter().collect())
.unwrap_or(HashMap::new());
Self::from_type_cuckoo(
realm,
cond,
cuckoo,
BlobTag(Blob {
blob_type: "re.fledg.tag".into(),
links,
datas: HashMap::new(),
values: [("name".to_string(), name.into())].into(),
}),
signers,
)
}
pub fn blob_id(&self) -> BlobID {
(*self.flo_id()).into()
}
}
impl BlobAccess for FloBlobTag {
fn get_blob(&self) -> &Blob {
&self.cache().0
}
fn get_blob_mut(&mut self) -> &mut Blob {
&mut self.cache_mut().0
}
}
impl BlobFamily for FloBlobTag {}
impl BlobPath for FloBlobTag {}
impl Blob {
pub fn new(blob_type: &str) -> Self {
Self {
blob_type: blob_type.to_string(),
links: HashMap::new(),
values: HashMap::new(),
datas: HashMap::new(),
}
}
}
impl BlobFamily for FloBlob {}
impl BlobPath for FloBlob {}
impl BlobAccess for FloBlob {
fn get_blob(&self) -> &Blob {
self.cache()
}
fn get_blob_mut(&mut self) -> &mut Blob {
self.cache_mut()
}
}
pub trait BlobAccess {
fn get_blob(&self) -> &Blob;
fn get_blob_mut(&mut self) -> &mut Blob;
fn links(&self) -> &HashMap<String, Vec<BlobID>> {
&self.get_blob().links
}
fn values(&self) -> &HashMap<String, String> {
&self.get_blob().values
}
fn datas(&self) -> &HashMap<String, Bytes> {
&self.get_blob().datas
}
fn get_data(&self, key: &str) -> Option<&Bytes> {
self.datas().get(key)
}
fn set_data(&mut self, key: String, value: Bytes) {
self.datas_mut().insert(key, value);
}
fn links_mut(&mut self) -> &mut HashMap<String, Vec<BlobID>> {
&mut self.get_blob_mut().links
}
fn values_mut(&mut self) -> &mut HashMap<String, String> {
&mut self.get_blob_mut().values
}
fn datas_mut(&mut self) -> &mut HashMap<String, Bytes> {
&mut self.get_blob_mut().datas
}
}
pub trait BlobFamily: BlobAccess {
fn set_parents(&mut self, parents: Vec<BlobID>) {
self.links_mut().insert("parents".into(), parents);
}
fn add_parent(&mut self, parent: BlobID) {
self.links_mut()
.entry("parents".into())
.or_insert_with(Vec::new)
.push(parent);
}
fn rm_parent(&mut self, parent: &BlobID) {
self.links_mut()
.get_mut("parents")
.map(|parents| parents.retain(|p| p != parent));
}
fn get_parents(&self) -> Vec<BlobID> {
self.links().get("parents").unwrap_or(&vec![]).clone()
}
fn set_children(&mut self, children: Vec<BlobID>) {
self.links_mut().insert("children".into(), children);
}
fn add_child(&mut self, child: BlobID) {
self.links_mut()
.entry("children".into())
.or_insert_with(Vec::new)
.push(child);
}
fn rm_child(&mut self, child: &BlobID) {
self.links_mut()
.get_mut("children")
.map(|children| children.retain(|c| c != child));
}
fn get_children(&self) -> Vec<BlobID> {
self.links().get("children").unwrap_or(&vec![]).clone()
}
}
pub trait BlobPath: BlobAccess {
fn set_path(&mut self, path: String) {
self.values_mut().insert("path".into(), path);
}
fn get_path(&self) -> Option<&String> {
self.values().get("path")
}
}
impl BlobAccess for Blob {
fn get_blob(&self) -> &Blob {
&self
}
fn get_blob_mut(&mut self) -> &mut Blob {
self
}
}
impl BlobAccess for BlobPage {
fn get_blob(&self) -> &Blob {
&self.0
}
fn get_blob_mut(&mut self) -> &mut Blob {
&mut self.0
}
}
impl BlobAccess for BlobTag {
fn get_blob(&self) -> &Blob {
&self.0
}
fn get_blob_mut(&mut self) -> &mut Blob {
&mut self.0
}
}
impl BlobPath for Blob {}
impl BlobFamily for Blob {}
impl BlobPath for BlobPage {}
impl BlobFamily for BlobPage {}
impl BlobPath for BlobTag {}
impl BlobFamily for BlobTag {}
pub trait BlobPathFamily: BlobPath + BlobFamily {}
impl<T: BlobPath + BlobFamily> BlobPathFamily for T {}
#[cfg(test)]
mod test {
use flcrypto::access::Condition;
use crate::{flo::crypto::FloBadge, testing::wallet::Wallet};
use super::*;
#[test]
fn test_update() -> anyhow::Result<()> {
let mut wallet = Wallet::new();
FloBadge::from_type(RealmID::rnd(), Condition::Fail, wallet.get_badge(), &[])?;
let flb = FloBlobPage::new(
RealmID::rnd(),
Condition::Verifier(wallet.get_verifier()),
"",
Bytes::from(""),
None,
&[&wallet.get_signer()],
)?;
let flb2 = flb.edit_data_signers(
Condition::Verifier(wallet.get_verifier()),
|bp| bp.set_parents(vec![BlobID::rnd()]),
&[&mut wallet.get_signer()],
)?;
assert_eq!(1, flb2.version());
let flb3 = flb2.edit_data_signers(
Condition::Verifier(wallet.get_verifier()),
|bp| {
bp.0.datas
.insert("index.html".into(), "<html><h1>Root</h1></html>".into());
},
&[&mut wallet.get_signer()],
)?;
assert_eq!(2, flb3.version());
Ok(())
}
}