use ant_evm::AttoTokens;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::{
collections::BTreeMap,
path::{Path, PathBuf},
};
use super::Metadata;
use crate::files::normalize_path;
use crate::{
Client,
client::{
GetError, PutError, data_types::chunk::DataMapChunk, high_level::files::RenameError,
payment::PaymentOption,
},
};
use bytes::Bytes;
use serde::{Deserialize, Serialize};
pub type PrivateArchiveDataMap = DataMapChunk;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct PrivateArchive {
map: BTreeMap<PathBuf, (DataMapChunk, Metadata)>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub enum PrivateArchiveVersioned {
V0(PrivateArchive),
}
impl PrivateArchive {
pub fn new() -> Self {
Self {
map: BTreeMap::new(),
}
}
pub fn rename_file(&mut self, old_path: &Path, new_path: &Path) -> Result<(), RenameError> {
let (data_addr, mut meta) = self
.map
.remove(old_path)
.ok_or(RenameError::FileNotFound(old_path.to_path_buf()))?;
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or(Duration::from_secs(0))
.as_secs();
meta.modified = now;
self.map.insert(new_path.to_path_buf(), (data_addr, meta));
debug!(
"Renamed file successfully in the private archive, old path: {old_path:?} new_path: {new_path:?}"
);
Ok(())
}
pub fn add_file(&mut self, path: PathBuf, data_map: DataMapChunk, meta: Metadata) {
let normalized_path = normalize_path(path.clone());
self.map.insert(normalized_path.clone(), (data_map, meta));
debug!(
"Added a new file to the archive, path: {:?}",
normalized_path
);
}
pub fn files(&self) -> Vec<(PathBuf, Metadata)> {
self.map
.iter()
.map(|(path, (_, meta))| (path.clone(), meta.clone()))
.collect()
}
pub fn data_maps(&self) -> Vec<DataMapChunk> {
self.map
.values()
.map(|(data_map, _)| data_map.clone())
.collect()
}
pub fn iter(&self) -> impl Iterator<Item = (&PathBuf, &DataMapChunk, &Metadata)> {
self.map
.iter()
.map(|(path, (data_map, meta))| (path, data_map, meta))
}
pub fn map(&self) -> &BTreeMap<PathBuf, (DataMapChunk, Metadata)> {
&self.map
}
pub fn from_bytes(data: Bytes) -> Result<PrivateArchive, rmp_serde::decode::Error> {
let root: PrivateArchiveVersioned = rmp_serde::from_slice(&data[..])?;
let PrivateArchiveVersioned::V0(root) = root;
Ok(root)
}
pub fn to_bytes(&self) -> Result<Bytes, rmp_serde::encode::Error> {
let versioned = PrivateArchiveVersioned::V0(self.clone());
let root_serialized = rmp_serde::to_vec_named(&versioned)?;
let root_serialized = Bytes::from(root_serialized);
Ok(root_serialized)
}
pub fn merge(&mut self, other: &PrivateArchive) {
self.map.extend(other.map.clone());
}
}
impl Client {
pub async fn archive_get(
&self,
addr: &PrivateArchiveDataMap,
) -> Result<PrivateArchive, GetError> {
let data = self.data_get(addr).await?;
Ok(PrivateArchive::from_bytes(data)?)
}
pub async fn archive_put(
&self,
archive: &PrivateArchive,
payment_option: PaymentOption,
) -> Result<(AttoTokens, PrivateArchiveDataMap), PutError> {
let bytes = archive
.to_bytes()
.map_err(|e| PutError::Serialization(format!("Failed to serialize archive: {e:?}")))?;
crate::loud_info!(
"Uploading private archive referencing {} files",
archive.map().len()
);
let data_map_chunk = self
.data_put(bytes, payment_option)
.await
.inspect_err(|err| {
error!("Error uploading private archive: {archive:?} err: {err:?}");
})?;
debug!(
"Uploaded private archive {archive:?} to the network and the private address is {:?}",
data_map_chunk.1.address()
);
Ok(data_map_chunk)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn test_private_archive_merge() {
let mut arch = PrivateArchive::new();
let file1 = PathBuf::from_str("file1").unwrap();
let file2 = PathBuf::from_str("file2").unwrap();
arch.add_file(
file1.clone(),
DataMapChunk::from_hex("1111").unwrap(),
Metadata::new_with_size(1),
);
let mut other_arch = PrivateArchive::new();
other_arch.add_file(
file2.clone(),
DataMapChunk::from_hex("AAAA").unwrap(),
Metadata::new_with_size(2),
);
arch.merge(&other_arch);
assert_eq!(arch.map().len(), 2);
assert_eq!(arch.map().get(&file1).unwrap().1.size, 1);
assert_eq!(arch.map().get(&file2).unwrap().1.size, 2);
let mut arch_with_duplicate = PrivateArchive::new();
arch_with_duplicate.add_file(
file1.clone(),
DataMapChunk::from_hex("BBBB").unwrap(),
Metadata::new_with_size(5),
);
arch.merge(&arch_with_duplicate);
assert_eq!(arch.map().len(), 2);
assert_eq!(arch.map().get(&file1).unwrap().1.size, 5);
assert_eq!(arch.map().get(&file2).unwrap().1.size, 2);
}
}