Import and update the file service application and orizentic #72

Merged
savanni merged 36 commits from file-service into main 2023-10-03 23:50:54 +00:00
4 changed files with 119 additions and 55 deletions
Showing only changes of commit 89a1aa7ee5 - Show all commits

View File

@ -6,6 +6,7 @@ use chrono::prelude::*;
use hex_string::HexString; use hex_string::HexString;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::{ use std::{
convert::TryFrom,
io::{Read, Write}, io::{Read, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
@ -26,8 +27,8 @@ impl File {
let id = FileId::from(Uuid::new_v4().hyphenated().to_string()); let id = FileId::from(Uuid::new_v4().hyphenated().to_string());
let mut path = context.root(); let mut path = context.root();
path.push((*id).to_owned()); path.push(filename.clone());
let path = PathResolver(path); let path = PathResolver::try_from(path).map_err(|_| WriteFileError::InvalidPath)?;
let file_type = mime_guess::from_ext(&filename) let file_type = mime_guess::from_ext(&filename)
.first_or_text_plain() .first_or_text_plain()
@ -190,7 +191,7 @@ impl File {
mod test { mod test {
use super::*; use super::*;
use crate::store::utils::FileCleanup; use crate::store::utils::FileCleanup;
use std::path::PathBuf; use std::{convert::TryFrom, path::PathBuf};
struct FileContext(PathBuf); struct FileContext(PathBuf);
@ -242,7 +243,7 @@ mod test {
#[test] #[test]
fn it_raises_an_error_when_file_not_found() { fn it_raises_an_error_when_file_not_found() {
let resolver = PathResolver(PathBuf::from("fixtures/rawr.png")); let resolver = PathResolver::try_from("fixtures/rawr.png").expect("a valid path");
match File::load(resolver) { match File::load(resolver) {
Err(ReadFileError::FileNotFound) => assert!(true), Err(ReadFileError::FileNotFound) => assert!(true),
_ => assert!(false), _ => assert!(false),

View File

@ -1,10 +1,11 @@
use super::{ReadFileError, WriteFileError};
use chrono::prelude::*; use chrono::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json; use serde_json;
use std::io::{Read, Write}; use std::{
use std::path::PathBuf; io::{Read, Write},
path::PathBuf,
use super::{ReadFileError, WriteFileError}; };
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FileInfo { pub struct FileInfo {
@ -95,12 +96,13 @@ impl FileInfo {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::store::utils::FileCleanup; use crate::store::{utils::FileCleanup, PathResolver};
use crate::store::PathResolver; use std::convert::TryFrom;
#[test] #[test]
fn it_saves_and_loads_metadata() { fn it_saves_and_loads_metadata() {
let resolver = PathResolver::from("fixtures/1617654d-a588-4714-b4fa-e00ed0a8a607"); let resolver = PathResolver::try_from("fixtures/1617654d-a588-4714-b4fa-e00ed0a8a607.png")
.expect("a valid path");
let _cleanup = FileCleanup(resolver.metadata_path()); let _cleanup = FileCleanup(resolver.metadata_path());
let created = Utc::now(); let created = Utc::now();

View File

@ -1,4 +1,5 @@
use std::{ use std::{
convert::TryFrom,
ops::Deref, ops::Deref,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
@ -22,6 +23,12 @@ pub enum WriteFileError {
#[error("permission denied")] #[error("permission denied")]
PermissionDenied, PermissionDenied,
#[error("invalid path")]
InvalidPath,
#[error("image conversion failed")]
ImageError(#[from] image::ImageError),
#[error("JSON error")] #[error("JSON error")]
JSONError(#[from] serde_json::error::Error), JSONError(#[from] serde_json::error::Error),
@ -40,6 +47,9 @@ pub enum ReadFileError {
#[error("permission denied")] #[error("permission denied")]
PermissionDenied, PermissionDenied,
#[error("invalid path")]
InvalidPath,
#[error("JSON error")] #[error("JSON error")]
JSONError(#[from] serde_json::error::Error), JSONError(#[from] serde_json::error::Error),
@ -47,36 +57,89 @@ pub enum ReadFileError {
IOError(#[from] std::io::Error), IOError(#[from] std::io::Error),
} }
#[derive(Debug, Error)]
pub enum PathError {
#[error("path cannot be derived from input")]
InvalidPath,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PathResolver(pub PathBuf); pub struct PathResolver {
base: PathBuf,
id: String,
extension: String,
}
impl PathResolver { impl PathResolver {
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 { fn file_path(&self) -> PathBuf {
self.0.clone() let mut path = self.base.clone();
path.push(self.id.clone());
path.set_extension(self.extension.clone());
path
} }
fn metadata_path(&self) -> PathBuf { fn metadata_path(&self) -> PathBuf {
let mut path = self.0.clone(); let mut path = self.base.clone();
path.push(self.id.clone());
path.set_extension("json"); path.set_extension("json");
path path
} }
fn thumbnail_path(&self) -> PathBuf { fn thumbnail_path(&self) -> PathBuf {
let mut path = self.0.clone(); let mut path = self.base.clone();
path.set_extension("tn"); path.push(self.id.clone());
path.set_extension(format!("tn.{}", self.extension));
path path
} }
} }
impl From<String> for PathResolver { impl TryFrom<String> for PathResolver {
fn from(s: String) -> Self { type Error = PathError;
Self(PathBuf::from(s)) fn try_from(s: String) -> Result<Self, Self::Error> {
PathResolver::try_from(s.as_str())
} }
} }
impl From<&str> for PathResolver { impl TryFrom<&str> for PathResolver {
fn from(s: &str) -> Self { type Error = PathError;
Self(PathBuf::from(s.to_owned())) 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)?,
})
} }
} }
@ -176,10 +239,11 @@ mod test {
#[test] #[test]
fn paths() { fn paths() {
let resolver = PathResolver::from("path/82420255-d3c8-4d90-a582-f94be588c70c"); let resolver = PathResolver::try_from("path/82420255-d3c8-4d90-a582-f94be588c70c.png")
.expect("to have a valid path");
assert_eq!( assert_eq!(
resolver.file_path(), resolver.file_path(),
PathBuf::from("path/82420255-d3c8-4d90-a582-f94be588c70c") PathBuf::from("path/82420255-d3c8-4d90-a582-f94be588c70c.png")
); );
assert_eq!( assert_eq!(
resolver.metadata_path(), resolver.metadata_path(),
@ -187,7 +251,7 @@ mod test {
); );
assert_eq!( assert_eq!(
resolver.thumbnail_path(), resolver.thumbnail_path(),
PathBuf::from("path/82420255-d3c8-4d90-a582-f94be588c70c.tn") PathBuf::from("path/82420255-d3c8-4d90-a582-f94be588c70c.tn.png")
); );
} }
@ -204,9 +268,9 @@ mod test {
let _md = FileCleanup(PathBuf::from(format!("var/{}.json", *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))); let _tn = FileCleanup(PathBuf::from(format!("var/{}.tn", *file_record.id)));
assert!(PathBuf::from(format!("var/{}", *file_record.id)).exists()); assert!(PathBuf::from(format!("var/{}.png", *file_record.id)).exists());
assert!(PathBuf::from(format!("var/{}.json", *file_record.id)).exists()); assert!(PathBuf::from(format!("var/{}.png.json", *file_record.id)).exists());
assert!(PathBuf::from(format!("var/{}.tn", *file_record.id)).exists()); assert!(PathBuf::from(format!("var/{}.png.tn", *file_record.id)).exists());
} }
#[test] #[test]

View File

@ -6,31 +6,25 @@ use super::{ReadFileError, WriteFileError};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Thumbnail { pub struct Thumbnail {
pub id: String, pub path: PathBuf,
pub root: PathBuf,
} }
impl Thumbnail { impl Thumbnail {
pub fn open(id: &str, root: &Path) -> Result<Thumbnail, WriteFileError> { pub fn open(
/* origin_path: PathBuf,
let mut source_path = PathBuf::from(root); thumbnail_path: PathBuf,
source_path.push(id); ) -> Result<Thumbnail, WriteFileError> {
let s = Thumbnail {
let self_ = Thumbnail { path: PathBuf::from(thumbnail_path),
id: String::from(id),
root: PathBuf::from(root),
}; };
let thumbnail_path = Thumbnail::thumbnail_path(id, root); if !s.path.exists() {
if !thumbnail_path.exists() { let img = image::open(&origin_path)?;
let img = image::open(source_path)?;
let tn = img.resize(640, 640, FilterType::Nearest); let tn = img.resize(640, 640, FilterType::Nearest);
tn.save(thumbnail_path)?; tn.save(&s.path)?;
} }
Ok(self_) Ok(s)
*/
unimplemented!()
} }
/* /*
@ -40,7 +34,7 @@ impl Thumbnail {
.map(|s| String::from(s.to_string_lossy())) .map(|s| String::from(s.to_string_lossy()))
.ok_or(ReadFileError::NotAnImage(PathBuf::from(path)))?; .ok_or(ReadFileError::NotAnImage(PathBuf::from(path)))?;
let root = path let path = path
.parent() .parent()
.ok_or(ReadFileError::FileNotFound(PathBuf::from(path)))?; .ok_or(ReadFileError::FileNotFound(PathBuf::from(path)))?;
@ -48,16 +42,17 @@ impl Thumbnail {
} }
*/ */
/*
fn thumbnail_path(id: &str, root: &Path) -> PathBuf { fn thumbnail_path(id: &str, root: &Path) -> PathBuf {
let mut path = PathBuf::from(root); let mut path = PathBuf::from(root);
path.push(".thumbnails"); path.push(".thumbnails");
path.push(id.clone()); path.push(id.clone());
path path
} }
*/
pub fn stream(&self) -> Result<std::fs::File, ReadFileError> { pub fn stream(&self) -> Result<std::fs::File, ReadFileError> {
let thumbnail_path = Thumbnail::thumbnail_path(&self.id, &self.root); std::fs::File::open(self.path.clone()).map_err(|err| {
std::fs::File::open(thumbnail_path.clone()).map_err(|err| {
if err.kind() == std::io::ErrorKind::NotFound { if err.kind() == std::io::ErrorKind::NotFound {
ReadFileError::FileNotFound ReadFileError::FileNotFound
} else { } else {
@ -66,9 +61,8 @@ impl Thumbnail {
}) })
} }
pub fn delete(&self) -> Result<(), WriteFileError> { pub fn delete(self) -> Result<(), WriteFileError> {
let path = Thumbnail::thumbnail_path(&self.id, &self.root); remove_file(self.path).map_err(WriteFileError::from)
remove_file(path).map_err(WriteFileError::from)
} }
} }
@ -79,9 +73,12 @@ mod test {
#[test] #[test]
fn it_creates_a_thumbnail_if_one_does_not_exist() { fn it_creates_a_thumbnail_if_one_does_not_exist() {
let _ = FileCleanup(PathBuf::from("fixtures/.thumbnails/rawr.png")); let _ = FileCleanup(PathBuf::from("var/rawr.tn.png"));
let _ = let _ = Thumbnail::open(
Thumbnail::open("rawr.png", Path::new("fixtures")).expect("thumbnail open must work"); PathBuf::from("fixtures/rawr.png"),
assert!(Path::new("fixtures/.thumbnails/rawr.png").is_file()); PathBuf::from("var/rawr.tn.png"),
)
.expect("thumbnail open must work");
assert!(Path::new("var/rawr.tn.png").is_file());
} }
} }