#![doc = include_str!("../readme.md")]
use std::borrow::Cow;
use std::io::Result;
use std::ops::RangeBounds;
use std::path::PathBuf;
use futures::Stream;
pub mod any;
pub mod http;
pub mod local;
pub mod noop;
pub mod pcloud;
pub(crate) mod util;
pub trait Store {
type Directory: StoreDirectory;
type File: StoreFile;
fn root(&self) -> impl Future<Output = Result<Self::Directory>> {
self.get_dir(PathBuf::default())
}
fn get_dir<P: Into<PathBuf>>(&self, path: P) -> impl Future<Output = Result<Self::Directory>>;
fn get_file<P: Into<PathBuf>>(&self, path: P) -> impl Future<Output = Result<Self::File>>;
}
pub trait StoreDirectory {
type Entry;
type Reader: StoreDirectoryReader<Self::Entry>;
fn name(&self) -> Option<Cow<'_, str>> {
self.path().file_name().map(|name| name.to_string_lossy())
}
fn path(&self) -> &std::path::Path;
fn exists(&self) -> impl Future<Output = Result<bool>>;
fn read(&self) -> impl Future<Output = Result<Self::Reader>>;
fn delete(&self) -> impl Future<Output = Result<()>>;
fn delete_recursive(&self) -> impl Future<Output = Result<()>>;
}
pub trait StoreDirectoryReader<E>: Stream<Item = Result<E>> + Sized {}
pub trait StoreFile {
type FileReader: StoreFileReader;
type FileWriter: StoreFileWriter;
type Metadata: StoreMetadata;
fn filename(&self) -> Option<Cow<'_, str>> {
self.path().file_name().map(|name| name.to_string_lossy())
}
fn path(&self) -> &std::path::Path;
fn exists(&self) -> impl Future<Output = Result<bool>>;
fn metadata(&self) -> impl Future<Output = Result<Self::Metadata>>;
fn read<R: RangeBounds<u64>>(&self, range: R)
-> impl Future<Output = Result<Self::FileReader>>;
fn write(&self, options: WriteOptions) -> impl Future<Output = Result<Self::FileWriter>>;
fn delete(&self) -> impl Future<Output = Result<()>>;
}
#[derive(Clone, Copy, Debug)]
enum WriteMode {
Append,
Truncate { offset: u64 },
}
#[derive(Clone, Debug)]
pub struct WriteOptions {
mode: WriteMode,
}
impl WriteOptions {
pub fn append() -> Self {
Self {
mode: WriteMode::Append,
}
}
pub fn create() -> Self {
Self {
mode: WriteMode::Truncate { offset: 0 },
}
}
pub fn truncate(offset: u64) -> Self {
Self {
mode: WriteMode::Truncate { offset },
}
}
}
pub trait StoreFileReader: tokio::io::AsyncRead {}
pub trait StoreFileWriter: tokio::io::AsyncWrite {}
#[derive(Debug)]
pub enum Entry<File, Directory> {
File(File),
Directory(Directory),
}
impl<File, Directory> Entry<File, Directory> {
pub fn is_directory(&self) -> bool {
matches!(self, Self::Directory(_))
}
pub fn is_file(&self) -> bool {
matches!(self, Self::File(_))
}
pub fn as_directory(&self) -> Option<&Directory> {
match self {
Self::Directory(inner) => Some(inner),
_ => None,
}
}
pub fn as_file(&self) -> Option<&File> {
match self {
Self::File(inner) => Some(inner),
_ => None,
}
}
pub fn into_directory(self) -> std::result::Result<Directory, Self> {
match self {
Self::Directory(inner) => Ok(inner),
other => Err(other),
}
}
pub fn into_file(self) -> std::result::Result<File, Self> {
match self {
Self::File(inner) => Ok(inner),
other => Err(other),
}
}
}
pub trait StoreMetadata {
fn size(&self) -> u64;
fn created(&self) -> u64;
fn modified(&self) -> u64;
fn content_type(&self) -> Option<&str> {
None
}
}
#[cfg(test)]
fn enable_tracing() {
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
let _ = tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "INFO".into()),
)
.with(tracing_subscriber::fmt::layer())
.try_init();
}