monorepo/file-service/src/store/mod.rs

288 lines
6.9 KiB
Rust
Raw Normal View History

use std::{
2023-09-23 19:17:49 +00:00
convert::TryFrom,
ops::Deref,
path::{Path, PathBuf},
};
use thiserror::Error;
use uuid::Uuid;
mod file;
mod fileinfo;
mod thumbnail;
pub mod utils;
pub use file::File;
pub use fileinfo::FileInfo;
pub use thumbnail::Thumbnail;
#[derive(Debug, Error)]
pub enum WriteFileError {
#[error("root file path does not exist")]
RootNotFound,
#[error("permission denied")]
PermissionDenied,
2023-09-23 19:17:49 +00:00
#[error("invalid path")]
InvalidPath,
#[error("image conversion failed")]
ImageError(#[from] image::ImageError),
#[error("JSON error")]
JSONError(#[from] serde_json::error::Error),
#[error("IO error")]
IOError(#[from] std::io::Error),
}
#[derive(Debug, Error)]
pub enum ReadFileError {
#[error("file not found")]
FileNotFound,
#[error("path is not a file")]
NotAFile,
#[error("permission denied")]
PermissionDenied,
2023-09-23 19:17:49 +00:00
#[error("invalid path")]
InvalidPath,
#[error("JSON error")]
JSONError(#[from] serde_json::error::Error),
#[error("IO error")]
IOError(#[from] std::io::Error),
}
2023-09-23 19:17:49 +00:00
#[derive(Debug, Error)]
pub enum PathError {
#[error("path cannot be derived from input")]
InvalidPath,
}
#[derive(Clone, Debug)]
2023-09-23 19:17:49 +00:00
pub struct PathResolver {
base: PathBuf,
id: String,
extension: String,
}
impl PathResolver {
2023-09-23 19:17:49 +00:00
fn new(base: &Path, id: &str, extension: &str) -> Self {
Self {
base: base.to_owned(),
id: id.to_owned(),
extension: extension.to_owned(),
}
}
fn file_path(&self) -> PathBuf {
2023-09-23 19:17:49 +00:00
let mut path = self.base.clone();
path.push(self.id.clone());
path.set_extension(self.extension.clone());
path
}
fn metadata_path(&self) -> PathBuf {
2023-09-23 19:17:49 +00:00
let mut path = self.base.clone();
path.push(self.id.clone());
path.set_extension("json");
path
}
fn thumbnail_path(&self) -> PathBuf {
2023-09-23 19:17:49 +00:00
let mut path = self.base.clone();
path.push(self.id.clone());
path.set_extension(format!("tn.{}", self.extension));
path
}
}
2023-09-23 19:17:49 +00:00
impl TryFrom<String> for PathResolver {
type Error = PathError;
fn try_from(s: String) -> Result<Self, Self::Error> {
PathResolver::try_from(s.as_str())
2023-09-23 01:56:43 +00:00
}
}
2023-09-23 19:17:49 +00:00
impl TryFrom<&str> for PathResolver {
type Error = PathError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
let path = Path::new(s);
PathResolver::try_from(Path::new(s))
}
}
impl TryFrom<PathBuf> for PathResolver {
type Error = PathError;
fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
PathResolver::try_from(path.as_path())
}
}
impl TryFrom<&Path> for PathResolver {
type Error = PathError;
fn try_from(path: &Path) -> Result<Self, Self::Error> {
Ok(Self {
base: path
.parent()
.map(|s| s.to_owned())
.ok_or(PathError::InvalidPath)?,
id: path
.file_stem()
.and_then(|s| s.to_str().map(|s| s.to_owned()))
.ok_or(PathError::InvalidPath)?,
extension: path
.extension()
.and_then(|s| s.to_str().map(|s| s.to_owned()))
.ok_or(PathError::InvalidPath)?,
})
2023-09-23 01:56:43 +00:00
}
}
#[derive(Clone, Debug)]
pub struct FileId(String);
impl From<String> for FileId {
fn from(s: String) -> Self {
Self(s)
}
}
impl From<&str> for FileId {
fn from(s: &str) -> Self {
Self(s.to_owned())
}
}
impl Deref for FileId {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub trait FileRoot {
fn root(&self) -> PathBuf;
}
pub struct Context(PathBuf);
impl FileRoot for Context {
fn root(&self) -> PathBuf {
self.0.clone()
}
}
pub struct Store {
files_root: PathBuf,
}
impl Store {
pub fn new(files_root: PathBuf) -> Self {
Self { files_root }
}
pub fn list_files(&self) -> Vec<Result<File, ReadFileError>> {
unimplemented!()
}
pub fn add_file(&mut self, filename: String, content: Vec<u8>) -> Result<File, WriteFileError> {
let context = Context(self.files_root.clone());
let mut file = File::new(filename, context)?;
file.set_content(content)?;
Ok(file)
}
pub fn delete_file(&mut self, id: String) -> Result<(), WriteFileError> {
/*
let f = File::open(&id, &self.files_root)?;
f.delete()
*/
unimplemented!()
}
pub fn get_metadata(&self, id: String) -> Result<FileInfo, ReadFileError> {
// FileInfo::open(&id, &self.files_root)
unimplemented!()
}
pub fn get_file(&self, id: &str) -> Result<(FileInfo, std::fs::File), ReadFileError> {
/*
let f = File::open(&id, &self.files_root)?;
let info = f.info();
let stream = f.stream()?;
Ok((info, stream))
*/
unimplemented!()
}
pub fn get_thumbnail(&self, id: &str) -> Result<(FileInfo, std::fs::File), ReadFileError> {
/*
let f = File::open(id, &self.files_root)?;
let stream = f.thumbnail().stream()?;
Ok((f.info(), stream))
*/
unimplemented!()
}
}
2023-09-23 01:56:43 +00:00
#[cfg(test)]
mod test {
use crate::store::utils::FileCleanup;
2023-09-23 01:56:43 +00:00
use super::*;
use std::io::Read;
2023-09-23 01:56:43 +00:00
#[test]
fn paths() {
2023-09-23 19:17:49 +00:00
let resolver = PathResolver::try_from("path/82420255-d3c8-4d90-a582-f94be588c70c.png")
.expect("to have a valid path");
2023-09-23 01:56:43 +00:00
assert_eq!(
resolver.file_path(),
2023-09-23 19:17:49 +00:00
PathBuf::from("path/82420255-d3c8-4d90-a582-f94be588c70c.png")
2023-09-23 01:56:43 +00:00
);
assert_eq!(
resolver.metadata_path(),
PathBuf::from("path/82420255-d3c8-4d90-a582-f94be588c70c.json")
);
assert_eq!(
resolver.thumbnail_path(),
2023-09-23 19:17:49 +00:00
PathBuf::from("path/82420255-d3c8-4d90-a582-f94be588c70c.tn.png")
2023-09-23 01:56:43 +00:00
);
}
#[test]
fn adds_files() {
let mut buf = Vec::new();
let mut file = std::fs::File::open("fixtures/rawr.png").unwrap();
file.read_to_end(&mut buf).unwrap();
let mut store = Store::new(PathBuf::from("var/"));
let file_record = store.add_file("rawr.png".to_owned(), buf).unwrap();
let _file = FileCleanup(PathBuf::from(format!("var/{}", *file_record.id)));
let _md = FileCleanup(PathBuf::from(format!("var/{}.json", *file_record.id)));
let _tn = FileCleanup(PathBuf::from(format!("var/{}.tn", *file_record.id)));
2023-09-23 19:17:49 +00:00
assert!(PathBuf::from(format!("var/{}.png", *file_record.id)).exists());
assert!(PathBuf::from(format!("var/{}.png.json", *file_record.id)).exists());
assert!(PathBuf::from(format!("var/{}.png.tn", *file_record.id)).exists());
}
#[test]
fn sets_up_metadata_for_file() {}
#[test]
fn sets_up_thumbnail_for_file() {}
#[test]
fn deletes_associated_files() {}
#[test]
fn lists_files_in_the_db() {}
2023-09-23 01:56:43 +00:00
}