use std::collections::BTreeMap;
use nectar_primitives::chunk::ChunkAddress;
use nectar_primitives::file::EntryRef;
use crate::mode::NodeEntry;
use crate::node::Node;
use crate::{MantarayError, Result, metadata};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Entry {
pub(crate) path: Vec<u8>,
pub(crate) reference: Option<EntryRef>,
pub(crate) metadata: BTreeMap<String, String>,
}
impl Entry {
pub fn new(reference: impl Into<EntryRef>) -> Self {
Self {
reference: Some(reference.into()),
path: Vec::new(),
metadata: BTreeMap::new(),
}
}
pub fn path(&self) -> &[u8] {
&self.path
}
pub const fn reference(&self) -> Option<&EntryRef> {
self.reference.as_ref()
}
pub const fn metadata(&self) -> &BTreeMap<String, String> {
&self.metadata
}
pub fn with_content_type(mut self, ct: &str) -> Self {
self.metadata
.insert(metadata::CONTENT_TYPE.into(), ct.into());
self
}
pub fn with_filename(mut self, name: &str) -> Self {
self.metadata.insert(metadata::FILENAME.into(), name.into());
self
}
pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.metadata.insert(key.into(), value.into());
self
}
pub fn content_type(&self) -> Option<&str> {
self.metadata
.get(metadata::CONTENT_TYPE)
.map(|s| s.as_str())
}
pub fn filename(&self) -> Option<&str> {
self.metadata.get(metadata::FILENAME).map(|s| s.as_str())
}
pub fn path_str(&self) -> Option<&str> {
std::str::from_utf8(&self.path).ok()
}
pub fn address(&self) -> Option<&ChunkAddress> {
self.reference.as_ref().map(|r| r.address())
}
pub(crate) fn from_node<E: NodeEntry>(path: &[u8], node: &Node<E>) -> Result<Self> {
let reference = match node.entry() {
Some(e) => {
let bytes = e.to_bytes();
Some(EntryRef::try_from_bytes(&bytes).map_err(|_| {
MantarayError::EntrySizeMismatch {
expected: E::SIZE,
actual: bytes.len(),
}
})?)
}
None => None,
};
let metadata = if node.metadata().is_empty() {
BTreeMap::new()
} else {
node.metadata().clone()
};
Ok(Self {
path: path.to_vec(),
reference,
metadata,
})
}
}
impl From<ChunkAddress> for Entry {
fn from(address: ChunkAddress) -> Self {
Self::new(address)
}
}
#[cfg(feature = "encryption")]
impl From<nectar_primitives::EncryptedChunkRef> for Entry {
fn from(enc_ref: nectar_primitives::EncryptedChunkRef) -> Self {
Self::new(enc_ref)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn entry_builder() {
let addr = ChunkAddress::from([1u8; 32]);
let entry = Entry::new(addr)
.with_content_type("image/png")
.with_filename("logo.png")
.with_metadata("custom-key", "custom-value");
assert_eq!(entry.address(), Some(&addr));
assert!(entry.path().is_empty());
assert_eq!(entry.content_type(), Some("image/png"));
assert_eq!(entry.filename(), Some("logo.png"));
assert_eq!(
entry.metadata().get("custom-key").map(|s| s.as_str()),
Some("custom-value")
);
}
#[test]
fn entry_builder_no_metadata() {
let addr = ChunkAddress::from([2u8; 32]);
let entry = Entry::new(addr);
assert_eq!(entry.content_type(), None);
assert_eq!(entry.filename(), None);
assert!(entry.metadata().is_empty());
}
}