use crate::client::{Client, MDataInfo};
use crate::crypto::shared_secretbox;
use crate::errors::CoreError;
use crate::nfs::{File, Mode, NfsError, Reader, Writer};
use crate::self_encryption_storage::SelfEncryptionStorage;
use bincode::{deserialize, serialize};
use log::trace;
use safe_nd::{Error as SndError, MDataSeqEntryActions};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum Version {
GetNext,
Custom(u64),
}
pub async fn insert<S>(
client: impl Client,
parent: MDataInfo,
name: S,
file: &File,
) -> Result<(), NfsError>
where
S: AsRef<str>,
{
let name = name.as_ref();
trace!("Inserting file with name '{}'", name);
let encoded = serialize(&file)?;
let key = parent.enc_entry_key(name.as_bytes())?;
let value = parent.enc_entry_value(&encoded)?;
client
.mutate_seq_mdata_entries(
parent.name(),
parent.type_tag(),
MDataSeqEntryActions::new().ins(key, value, 0),
)
.await
.map_err(From::from)
}
pub async fn fetch<S>(
client: impl Client,
parent: MDataInfo,
name: S,
) -> Result<(u64, File), NfsError>
where
S: AsRef<str>,
{
let key = parent.enc_entry_key(name.as_ref().as_bytes())?;
let value = client
.get_seq_mdata_value(parent.name(), parent.type_tag(), key)
.await
.map_err(convert_error)?;
let plaintext = parent.decrypt(&value.data)?;
let file = deserialize(&plaintext)?;
Ok((value.version, file))
}
pub async fn read<C: Client + 'static>(
client: C,
file: &File,
encryption_key: Option<shared_secretbox::Key>,
) -> Result<Reader<C>, NfsError> {
trace!("Reading file {:?}", file);
Reader::new(
client.clone(),
SelfEncryptionStorage::new(client, file.published()),
file,
encryption_key,
)
.await
}
#[allow(clippy::needless_pass_by_value)]
pub async fn delete<S>(
client: impl Client,
parent: MDataInfo,
name: S,
published: bool,
version: Version,
) -> Result<u64, NfsError>
where
S: AsRef<str>,
{
let name = name.as_ref();
let name2 = name.to_owned();
let client2 = client.clone();
let client3 = client.clone();
let parent2 = parent.clone();
trace!("Deleting file with name {}.", name);
let key = parent.enc_entry_key(name.as_bytes())?;
let new_version = match version {
Version::GetNext => {
let value = client
.get_seq_mdata_value(parent.name(), parent.type_tag(), key.clone())
.await
.map_err(convert_error)?;
value.version + 1
}
Version::Custom(version) => version,
};
if !published {
let (_, file) = fetch(client, parent2, name2).await?;
client2.del_unpub_idata(*file.data_map_name()).await?;
}
client3
.mutate_seq_mdata_entries(
parent.name(),
parent.type_tag(),
MDataSeqEntryActions::new().del(key, new_version),
)
.await
.map_err(convert_error)?;
Ok(new_version)
}
pub async fn update<S>(
client: impl Client,
parent: MDataInfo,
name: S,
file: &File,
version: Version,
) -> Result<u64, NfsError>
where
S: AsRef<str>,
{
let name = name.as_ref();
trace!("Updating file with name '{}'", name);
let client2 = client.clone();
let encoded = serialize(&file)?;
let key = parent.enc_entry_key(name.as_bytes())?;
let content = parent.enc_entry_value(&encoded)?;
let version = match version {
Version::GetNext => {
let value = client
.get_seq_mdata_value(parent.name(), parent.type_tag(), key.clone())
.await
.map_err(convert_error)?;
value.version + 1
}
Version::Custom(version) => version,
};
client2
.mutate_seq_mdata_entries(
parent.name(),
parent.type_tag(),
MDataSeqEntryActions::new().update(key, content, version),
)
.await
.map_err(convert_error)?;
Ok(version)
}
pub async fn write<C: Client + 'static>(
client: C,
file: File,
mode: Mode,
encryption_key: Option<shared_secretbox::Key>,
) -> Result<Writer<C>, NfsError> {
trace!("Creating a writer for a file");
Writer::new(
&client.clone(),
SelfEncryptionStorage::new(client, file.published()),
file,
mode,
encryption_key,
)
.await
}
fn convert_error(err: CoreError) -> NfsError {
match err {
CoreError::DataError(SndError::NoSuchEntry) => NfsError::FileNotFound,
_ => NfsError::from(err),
}
}