#![allow(clippy::doc_lazy_continuation)]
use std::collections::BTreeMap;
use std::path::Path;
use mime::Mime;
use serde::{Deserialize, Serialize};
use crate::crypto::Secret;
use crate::linked_data::{BlockEncoded, DagCborCodec, Link, LinkedData};
use super::maybe_mime::MaybeMime;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Data {
mime: MaybeMime,
metadata: Option<BTreeMap<String, LinkedData>>,
}
impl Default for Data {
fn default() -> Self {
Self::new()
}
}
impl Data {
pub fn new() -> Self {
Self {
mime: MaybeMime(None),
metadata: None,
}
}
pub fn from_path(path: &Path) -> Self {
let mime = MaybeMime::from_path(path);
let metadata = BTreeMap::new();
Self {
mime,
metadata: if metadata.is_empty() {
None
} else {
Some(metadata)
},
}
}
pub fn set_metadata(&mut self, key: String, value: LinkedData) {
if let Some(ref mut metadata) = self.metadata {
metadata.insert(key, value);
} else {
let mut metadata = BTreeMap::new();
metadata.insert(key, value);
self.metadata = Some(metadata);
}
}
pub fn mime(&self) -> Option<&Mime> {
self.mime.0.as_ref()
}
pub fn metadata(&self) -> Option<&BTreeMap<String, LinkedData>> {
self.metadata.as_ref()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum NodeLink {
Data(Link, Secret, Data),
Dir(Link, Secret),
}
impl NodeLink {
pub fn new_data_from_path(link: Link, secret: Secret, path: &Path) -> Self {
let data = Data::from_path(path);
NodeLink::Data(link, secret, data)
}
pub fn new_data(link: Link, secret: Secret) -> Self {
NodeLink::Data(link, secret, Data::new())
}
pub fn new_dir(link: Link, secret: Secret) -> Self {
NodeLink::Dir(link, secret)
}
pub fn link(&self) -> &Link {
match self {
NodeLink::Data(link, _, _) => link,
NodeLink::Dir(link, _) => link,
}
}
pub fn secret(&self) -> &Secret {
match self {
NodeLink::Data(_, secret, _) => secret,
NodeLink::Dir(_, secret) => secret,
}
}
pub fn data(&self) -> Option<&Data> {
match self {
NodeLink::Data(_, _, data) => Some(data),
NodeLink::Dir(_, _) => None,
}
}
pub fn is_dir(&self) -> bool {
matches!(self, NodeLink::Dir(_, _))
}
pub fn is_data(&self) -> bool {
matches!(self, NodeLink::Data(_, _, _))
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct Node {
links: BTreeMap<String, NodeLink>,
}
#[derive(Debug, thiserror::Error)]
pub enum NodeError {
#[error("link not found")]
LinkNotFound(String),
}
impl BlockEncoded<DagCborCodec> for Node {}
impl Node {
pub fn new() -> Self {
Node {
links: BTreeMap::new(),
}
}
pub fn get_link(&self, name: &str) -> Option<&NodeLink> {
self.links.get(name)
}
pub fn insert(&mut self, name: String, link: NodeLink) -> Option<NodeLink> {
self.links.insert(name, link)
}
pub fn get_links(&self) -> &BTreeMap<String, NodeLink> {
&self.links
}
pub fn del(&mut self, name: &str) -> Option<NodeLink> {
self.links.remove(name)
}
pub fn size(&self) -> usize {
self.links.len()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_node_encode_decode() {
let mut node = Node::default();
node.links.insert(
"example".to_string(),
NodeLink::Data(
Link::default(),
Secret::default(),
Data {
metadata: None,
mime: MaybeMime(None),
},
),
);
let encoded = node.encode().unwrap();
let decoded = Node::decode(&encoded).unwrap();
assert_eq!(node, decoded);
}
#[test]
fn test_data_from_path() {
use std::path::PathBuf;
let path = PathBuf::from("/test/file.json");
let data = Data::from_path(&path);
assert_eq!(data.mime().map(|m| m.as_ref()), Some("application/json"));
let path = PathBuf::from("/src/main.rs");
let data = Data::from_path(&path);
assert_eq!(data.mime().map(|m| m.as_ref()), Some("text/x-rust"));
let path = PathBuf::from("/audio/song.m4a");
let data = Data::from_path(&path);
assert_eq!(data.mime().map(|m| m.as_ref()), Some("audio/m4a"));
let path = PathBuf::from("/test/file.unknownext");
let data = Data::from_path(&path);
assert_eq!(data.mime(), None);
let path = PathBuf::from("/test/README");
let data = Data::from_path(&path);
assert_eq!(data.mime(), None);
}
#[test]
fn test_node_link_constructors() {
use std::path::PathBuf;
let link = Link::default();
let secret = Secret::default();
let path = PathBuf::from("/test/image.png");
let node_link = NodeLink::new_data_from_path(link.clone(), secret.clone(), &path);
assert!(node_link.is_data());
assert!(!node_link.is_dir());
let data = node_link.data().unwrap();
assert_eq!(data.mime().map(|m| m.as_ref()), Some("image/png"));
let node_link = NodeLink::new_data(link.clone(), secret.clone());
assert!(node_link.is_data());
let data = node_link.data().unwrap();
assert_eq!(data.mime(), None);
assert_eq!(data.metadata(), None);
let node_link = NodeLink::new_dir(link.clone(), secret.clone());
assert!(node_link.is_dir());
assert!(!node_link.is_data());
assert!(node_link.data().is_none());
}
}