use crate::CryptoError;
use base64::DecodeError;
use once_cell::sync::OnceCell;
use serde::{
de::{self, Deserializer},
Deserialize, Serialize, Serializer,
};
use std::{
convert::Into,
error::Error,
fmt::{self, Display, Formatter},
io::{self, ErrorKind},
path::PathBuf as StdPathBuf,
str::FromStr,
};
#[derive(Debug)]
pub enum NotFoundKind {
File(String),
Vector,
}
#[derive(Debug)]
pub enum SourceError {
FsIoError { source: io::Error },
NotFound { kind: NotFoundKind },
FilePathHasNoFileStem { path: String },
FilePathIsInvalidUTF8,
Base64Decode { source: DecodeError },
}
impl Error for SourceError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match *self {
SourceError::FsIoError { ref source } => Some(source),
SourceError::NotFound { .. } => None,
SourceError::FilePathHasNoFileStem { .. } => None,
SourceError::FilePathIsInvalidUTF8 => None,
SourceError::Base64Decode { ref source } => Some(source),
}
}
}
impl Display for SourceError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
SourceError::FsIoError { .. } => {
write!(f, "Error occured during file system I/O")
}
SourceError::NotFound { ref kind } => match kind {
NotFoundKind::File(path) => write!(f, "Path \"{}\" not found", path),
NotFoundKind::Vector => write!(f, "Vector byte source contains no bytes"),
},
SourceError::FilePathHasNoFileStem { ref path } => {
write!(
f,
"File path \"{}\" was invalid as the file name has no stem",
path
)
}
SourceError::FilePathIsInvalidUTF8 => {
write!(f, "Given file path was not valid UTF-8")
}
SourceError::Base64Decode { .. } => {
write!(f, "Error occurred while decoding string from base64")
}
}
}
}
impl From<SourceError> for CryptoError {
fn from(mse: SourceError) -> Self {
match mse {
SourceError::NotFound { .. } => CryptoError::NotFound {
source: Box::new(mse),
},
_ => CryptoError::InternalError {
source: Box::new(mse),
},
}
}
}
pub trait HasByteSource {
fn byte_source(&self) -> ByteSource;
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "t", content = "c")]
pub enum Source {
Byte(ByteSource),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "t", content = "c")]
pub enum ByteSource {
Fs(FsByteSource),
Vector(VectorByteSource),
}
impl ByteSource {
pub fn set(&mut self, key: &[u8]) -> Result<(), SourceError> {
match self {
ByteSource::Fs(fsbks) => fsbks.set(key),
ByteSource::Vector(vbks) => vbks.set(key),
}
}
pub fn get(&self) -> Result<&[u8], SourceError> {
match self {
ByteSource::Fs(fsbks) => fsbks.get(),
ByteSource::Vector(vbks) => vbks.get(),
}
}
}
impl From<&[u8]> for ByteSource {
fn from(value: &[u8]) -> Self {
ByteSource::Vector(value.into())
}
}
impl From<&str> for ByteSource {
fn from(value: &str) -> Self {
ByteSource::Vector(value.into())
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Path {
path: StdPathBuf,
stem: String,
}
impl Path {
pub fn file_stem(&self) -> &str {
&self.stem
}
}
impl<'a> From<&'a Path> for &'a StdPathBuf {
fn from(path: &'a Path) -> Self {
&path.path
}
}
impl FromStr for Path {
type Err = SourceError;
fn from_str(path: &str) -> Result<Self, Self::Err> {
let path: StdPathBuf = path.into();
let stem = path
.file_stem()
.ok_or(SourceError::FilePathHasNoFileStem {
path: path
.clone()
.into_os_string()
.into_string()
.unwrap_or_else(|_| "<Invalid UTF8>".to_owned()),
})?
.to_str()
.ok_or(SourceError::FilePathIsInvalidUTF8)?
.to_owned();
Ok(Self { path, stem })
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FsByteSource {
path: Path,
#[serde(skip)]
cached: OnceCell<VectorByteSource>,
}
impl FromStr for FsByteSource {
type Err = SourceError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let path = Path::from_str(s)?;
Ok(FsByteSource::new(path))
}
}
impl FsByteSource {
pub fn new(path: Path) -> Self {
let cached = OnceCell::new();
FsByteSource { path, cached }
}
fn read_from_path(path: &Path) -> Result<VectorByteSource, SourceError> {
let path_ref: &StdPathBuf = path.into();
let path_str = path
.path
.clone()
.into_os_string()
.into_string()
.unwrap_or_else(|_| "<Invalid UTF8>".to_owned());
let read_bytes = std::fs::read(path_ref).map_err(|e| match e.kind() {
ErrorKind::NotFound => SourceError::NotFound {
kind: NotFoundKind::File(path_str),
},
_ => SourceError::FsIoError { source: e },
})?;
let bytes =
base64::decode(read_bytes).map_err(|e| SourceError::Base64Decode { source: e })?;
Ok(VectorByteSource { value: Some(bytes) })
}
pub fn reload(&mut self) {
self.cached.take();
}
pub fn set(&mut self, value: &[u8]) -> Result<(), SourceError> {
let path_ref: &StdPathBuf = (&self.path).into();
let path_str = self
.path
.path
.clone()
.into_os_string()
.into_string()
.unwrap_or_else(|_| "<Invalid UTF8>".to_owned());
let path_parent = path_ref.parent();
let bytes = base64::encode(value);
if let Some(path) = path_parent {
std::fs::create_dir_all(path).map_err(|source| SourceError::FsIoError { source })?;
}
std::fs::write(path_ref, bytes)
.map(|_| self.reload())
.map_err(|source| match source.kind() {
std::io::ErrorKind::NotFound => SourceError::NotFound {
kind: NotFoundKind::File(path_str),
},
_ => SourceError::FsIoError { source },
})
}
pub fn get(&self) -> Result<&[u8], SourceError> {
self.cached
.get_or_try_init(|| Self::read_from_path(&self.path))?
.get()
}
pub fn path(&self) -> &Path {
&self.path
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VectorByteSource {
#[serde(
serialize_with = "byte_vector_serialize",
deserialize_with = "byte_vector_deserialize"
)]
value: Option<Vec<u8>>,
}
fn byte_vector_serialize<S>(bytes: &Option<Vec<u8>>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match bytes {
Some(bytes) => {
let b64_encoded = base64::encode(bytes);
s.serialize_some(&Some(b64_encoded))
}
None => s.serialize_none(),
}
}
fn byte_vector_deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
where
D: Deserializer<'de>,
{
let b64_encoded: Option<String> = de::Deserialize::deserialize(deserializer)?;
match b64_encoded {
Some(bytes) => Ok(Some(base64::decode(bytes).map_err(de::Error::custom)?)),
None => Ok(None),
}
}
impl VectorByteSource {
pub fn new(value: Option<&[u8]>) -> Self {
match value {
Some(value) => VectorByteSource {
value: Some(value.to_vec()),
},
None => VectorByteSource { value: None },
}
}
pub fn set(&mut self, key: &[u8]) -> Result<(), SourceError> {
self.value = Some(key.to_owned());
Ok(())
}
pub fn get(&self) -> Result<&[u8], SourceError> {
match self.value {
Some(ref bytes) => Ok(bytes.as_ref()),
None => Err(SourceError::NotFound {
kind: NotFoundKind::Vector,
}),
}
}
}
impl From<&[u8]> for VectorByteSource {
fn from(value: &[u8]) -> Self {
Self::new(Some(value))
}
}
impl From<&str> for VectorByteSource {
fn from(value: &str) -> Self {
Self::new(Some(value.as_ref()))
}
}