Compare commits
5 Commits
6c1fa7cc2f
...
3294262d8d
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | 3294262d8d | |
Savanni D'Gerinel | afa8134644 | |
Savanni D'Gerinel | f7012ee11a | |
Savanni D'Gerinel | 759a4b6a1a | |
Savanni D'Gerinel | 4fa3a89e08 |
|
@ -692,6 +692,7 @@ dependencies = [
|
||||||
"serde 1.0.188",
|
"serde 1.0.188",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"tempdir",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uuid 0.4.0",
|
"uuid 0.4.0",
|
||||||
|
|
|
@ -32,4 +32,4 @@ log = { version = "0.4" }
|
||||||
bytes = { version = "1" }
|
bytes = { version = "1" }
|
||||||
futures-util = { version = "0.3" }
|
futures-util = { version = "0.3" }
|
||||||
cool_asserts = { version = "2" }
|
cool_asserts = { version = "2" }
|
||||||
|
tempdir = { version = "0.3" }
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
{"id":"rawr.png","size":23777,"created":"2020-06-04T16:04:10.085680927Z","file_type":"image/png","hash":"b6cd35e113b95d62f53d9cbd27ccefef47d3e324aef01a2db6c0c6d3a43c89ee"}
|
|
Binary file not shown.
Before Width: | Height: | Size: 48 KiB |
|
@ -1,11 +1,3 @@
|
||||||
/*
|
|
||||||
use iron::headers;
|
|
||||||
use iron::middleware::Handler;
|
|
||||||
use iron::modifiers::{Header, Redirect};
|
|
||||||
use iron::prelude::*;
|
|
||||||
use iron::response::BodyReader;
|
|
||||||
use iron::status;
|
|
||||||
*/
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
@ -14,9 +6,8 @@ use http::status::StatusCode;
|
||||||
// use orizentic::{Permissions, ResourceName, Secret};
|
// use orizentic::{Permissions, ResourceName, Secret};
|
||||||
use build_html::Html;
|
use build_html::Html;
|
||||||
use bytes::Buf;
|
use bytes::Buf;
|
||||||
use futures_util::{Stream, StreamExt};
|
use futures_util::StreamExt;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
io::Read,
|
io::Read,
|
||||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
@ -24,9 +15,9 @@ use std::{
|
||||||
};
|
};
|
||||||
use warp::{filters::multipart::Part, Filter};
|
use warp::{filters::multipart::Part, Filter};
|
||||||
|
|
||||||
mod cookies;
|
// mod cookies;
|
||||||
mod html;
|
mod html;
|
||||||
mod middleware;
|
// mod middleware;
|
||||||
mod pages;
|
mod pages;
|
||||||
mod store;
|
mod store;
|
||||||
|
|
||||||
|
@ -430,7 +421,7 @@ pub async fn main() {
|
||||||
.with(log),
|
.with(log),
|
||||||
);
|
);
|
||||||
*/
|
*/
|
||||||
let server = warp::serve(root.or(upload).with(log));
|
let server = warp::serve(root.or(upload).or(thumbnail).or(file).or(delete).with(log));
|
||||||
server
|
server
|
||||||
.run(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8002))
|
.run(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8002))
|
||||||
.await;
|
.await;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
html::*,
|
html::*,
|
||||||
store::{FileHandle, FileId, ReadFileError, Thumbnail},
|
store::{FileHandle, FileId, ReadFileError},
|
||||||
};
|
};
|
||||||
use build_html::{self, Container, ContainerType, Html, HtmlContainer};
|
use build_html::{self, Container, ContainerType, Html, HtmlContainer};
|
||||||
|
|
||||||
|
|
|
@ -21,27 +21,34 @@ pub enum PathError {
|
||||||
pub struct PathResolver {
|
pub struct PathResolver {
|
||||||
base: PathBuf,
|
base: PathBuf,
|
||||||
id: FileId,
|
id: FileId,
|
||||||
|
extension: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PathResolver {
|
impl PathResolver {
|
||||||
pub fn new(base: &Path, id: FileId) -> Self {
|
pub fn new(base: &Path, id: FileId, extension: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
base: base.to_owned(),
|
base: base.to_owned(),
|
||||||
id,
|
id,
|
||||||
|
extension,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn metadata_path_by_id(base: &Path, id: FileId) -> PathBuf {
|
||||||
|
let mut path = base.to_path_buf();
|
||||||
|
path.push(PathBuf::from(id.clone()));
|
||||||
|
path.set_extension("json");
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> FileId {
|
pub fn id(&self) -> FileId {
|
||||||
self.id.clone()
|
self.id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_path(&self) -> Result<PathBuf, ReadFileError> {
|
pub fn file_path(&self) -> PathBuf {
|
||||||
let info = FileInfo::load(self.metadata_path())?;
|
|
||||||
|
|
||||||
let mut path = self.base.clone();
|
let mut path = self.base.clone();
|
||||||
path.push(PathBuf::from(self.id.clone()));
|
path.push(PathBuf::from(self.id.clone()));
|
||||||
path.set_extension(info.extension);
|
path.set_extension(self.extension.clone());
|
||||||
Ok(path)
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn metadata_path(&self) -> PathBuf {
|
pub fn metadata_path(&self) -> PathBuf {
|
||||||
|
@ -51,13 +58,11 @@ impl PathResolver {
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thumbnail_path(&self) -> Result<PathBuf, ReadFileError> {
|
pub fn thumbnail_path(&self) -> PathBuf {
|
||||||
let info = FileInfo::load(self.metadata_path())?;
|
|
||||||
|
|
||||||
let mut path = self.base.clone();
|
let mut path = self.base.clone();
|
||||||
path.push(PathBuf::from(self.id.clone()));
|
path.push(PathBuf::from(self.id.clone()));
|
||||||
path.set_extension(format!("tn.{}", info.extension));
|
path.set_extension(format!("tn.{}", self.extension));
|
||||||
Ok(path)
|
path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +76,6 @@ impl TryFrom<String> for PathResolver {
|
||||||
impl TryFrom<&str> for PathResolver {
|
impl TryFrom<&str> for PathResolver {
|
||||||
type Error = PathError;
|
type Error = PathError;
|
||||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
let path = Path::new(s);
|
|
||||||
PathResolver::try_from(Path::new(s))
|
PathResolver::try_from(Path::new(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,6 +99,10 @@ impl TryFrom<&Path> for PathResolver {
|
||||||
.file_stem()
|
.file_stem()
|
||||||
.and_then(|s| s.to_str().map(|s| FileId::from(s)))
|
.and_then(|s| s.to_str().map(|s| FileId::from(s)))
|
||||||
.ok_or(PathError::InvalidPath)?,
|
.ok_or(PathError::InvalidPath)?,
|
||||||
|
extension: path
|
||||||
|
.extension()
|
||||||
|
.and_then(|s| s.to_str().map(|s| s.to_owned()))
|
||||||
|
.ok_or(PathError::InvalidPath)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,6 +128,7 @@ impl FileHandle {
|
||||||
let path = PathResolver {
|
let path = PathResolver {
|
||||||
base: root.clone(),
|
base: root.clone(),
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
|
extension: extension.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let file_type = mime_guess::from_ext(&extension)
|
let file_type = mime_guess::from_ext(&extension)
|
||||||
|
@ -143,8 +152,8 @@ impl FileHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(id: &FileId, root: &Path) -> Result<Self, ReadFileError> {
|
pub fn load(id: &FileId, root: &Path) -> Result<Self, ReadFileError> {
|
||||||
let resolver = PathResolver::new(root, id.clone());
|
let info = FileInfo::load(PathResolver::metadata_path_by_id(root, id.clone()))?;
|
||||||
let info = FileInfo::load(resolver.metadata_path())?;
|
let resolver = PathResolver::new(root, id.clone(), info.extension.clone());
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
id: info.id.clone(),
|
id: info.id.clone(),
|
||||||
path: resolver,
|
path: resolver,
|
||||||
|
@ -153,69 +162,42 @@ impl FileHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_content(&mut self, content: Vec<u8>) -> Result<(), WriteFileError> {
|
pub fn set_content(&mut self, content: Vec<u8>) -> Result<(), WriteFileError> {
|
||||||
let mut content_file = std::fs::File::create(
|
let mut content_file = std::fs::File::create(self.path.file_path())?;
|
||||||
self.path
|
|
||||||
.file_path()
|
|
||||||
.map_err(|_| WriteFileError::NoMetadata)?,
|
|
||||||
)?;
|
|
||||||
let byte_count = content_file.write(&content)?;
|
let byte_count = content_file.write(&content)?;
|
||||||
self.info.size = byte_count;
|
self.info.size = byte_count;
|
||||||
|
self.info.hash = self.hash_content(&content).as_string();
|
||||||
|
|
||||||
let mut md_file = std::fs::File::create(self.path.metadata_path())?;
|
let mut md_file = std::fs::File::create(self.path.metadata_path())?;
|
||||||
md_file.write(&serde_json::to_vec(&self.info)?)?;
|
md_file.write(&serde_json::to_vec(&self.info)?)?;
|
||||||
|
|
||||||
self.write_thumbnail();
|
self.write_thumbnail()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn content(&self) -> Result<Vec<u8>, ReadFileError> {
|
pub fn content(&self) -> Result<Vec<u8>, ReadFileError> {
|
||||||
load_content(&self.path.file_path()?)
|
load_content(&self.path.file_path())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thumbnail(&self) -> Result<Vec<u8>, ReadFileError> {
|
pub fn thumbnail(&self) -> Result<Vec<u8>, ReadFileError> {
|
||||||
load_content(&self.path.thumbnail_path()?)
|
load_content(&self.path.thumbnail_path())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_content(&self) -> Result<HexString, ReadFileError> {
|
fn hash_content(&self, data: &Vec<u8>) -> HexString {
|
||||||
let mut buf = Vec::new();
|
HexString::from_bytes(&Sha256::digest(data).to_vec())
|
||||||
let mut file = std::fs::File::open(self.path.file_path()?)?;
|
|
||||||
file.read_to_end(&mut buf).map_err(ReadFileError::from)?;
|
|
||||||
|
|
||||||
Ok(HexString::from_bytes(&Sha256::digest(&buf).to_vec()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_thumbnail(&self) -> Result<(), WriteFileError> {
|
fn write_thumbnail(&self) -> Result<(), WriteFileError> {
|
||||||
let img = image::open(
|
let img = image::open(&self.path.file_path())?;
|
||||||
&self
|
|
||||||
.path
|
|
||||||
.file_path()
|
|
||||||
.map_err(|_| WriteFileError::NoMetadata)?,
|
|
||||||
)?;
|
|
||||||
let tn = img.resize(640, 640, FilterType::Nearest);
|
let tn = img.resize(640, 640, FilterType::Nearest);
|
||||||
tn.save(
|
tn.save(&self.path.thumbnail_path())?;
|
||||||
&self
|
|
||||||
.path
|
|
||||||
.thumbnail_path()
|
|
||||||
.map_err(|_| WriteFileError::NoMetadata)?,
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(self) -> Result<(), WriteFileError> {
|
pub fn delete(self) {
|
||||||
std::fs::remove_file(
|
let _ = std::fs::remove_file(self.path.thumbnail_path());
|
||||||
self.path
|
let _ = std::fs::remove_file(self.path.file_path());
|
||||||
.thumbnail_path()
|
let _ = std::fs::remove_file(self.path.metadata_path());
|
||||||
.map_err(|_| WriteFileError::NoMetadata)?,
|
|
||||||
)?;
|
|
||||||
std::fs::remove_file(self.path.metadata_path())?;
|
|
||||||
std::fs::remove_file(
|
|
||||||
self.path
|
|
||||||
.file_path()
|
|
||||||
.map_err(|_| WriteFileError::NoMetadata)?,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,43 +211,47 @@ fn load_content(path: &Path) -> Result<Vec<u8>, ReadFileError> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::store::utils::FileCleanup;
|
|
||||||
use cool_asserts::assert_matches;
|
|
||||||
use std::{convert::TryFrom, path::PathBuf};
|
use std::{convert::TryFrom, path::PathBuf};
|
||||||
|
use tempdir::TempDir;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn paths() {
|
fn paths() {
|
||||||
let resolver = PathResolver::try_from("path/82420255-d3c8-4d90-a582-f94be588c70c.png")
|
let resolver = PathResolver::try_from("path/82420255-d3c8-4d90-a582-f94be588c70c.png")
|
||||||
.expect("to have a valid path");
|
.expect("to have a valid path");
|
||||||
assert_matches!(
|
|
||||||
|
assert_eq!(
|
||||||
resolver.file_path(),
|
resolver.file_path(),
|
||||||
Ok(path) => assert_eq!(path, PathBuf::from(
|
PathBuf::from("path/82420255-d3c8-4d90-a582-f94be588c70c.png")
|
||||||
"path/82420255-d3c8-4d90-a582-f94be588c70c.png"
|
|
||||||
))
|
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolver.metadata_path(),
|
resolver.metadata_path(),
|
||||||
PathBuf::from("path/82420255-d3c8-4d90-a582-f94be588c70c.json")
|
PathBuf::from("path/82420255-d3c8-4d90-a582-f94be588c70c.json")
|
||||||
);
|
);
|
||||||
assert_matches!(
|
assert_eq!(
|
||||||
resolver.thumbnail_path(),
|
resolver.thumbnail_path(),
|
||||||
Ok(path) => assert_eq!(path, PathBuf::from(
|
PathBuf::from("path/82420255-d3c8-4d90-a582-f94be588c70c.tn.png")
|
||||||
"path/82420255-d3c8-4d90-a582-f94be588c70c.tn.png"
|
|
||||||
))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_opens_a_file() {
|
fn it_opens_a_file() {
|
||||||
let _md = FileCleanup(PathBuf::from("fixtures/.metadata/rawr.png.json"));
|
let tmp = TempDir::new("var").unwrap();
|
||||||
let _tn = FileCleanup(PathBuf::from("fixtures/.thumbnails/rawr.png"));
|
FileHandle::new("rawr.png".to_owned(), PathBuf::from(tmp.path())).expect("to succeed");
|
||||||
|
}
|
||||||
|
|
||||||
FileHandle::new("rawr.png".to_owned(), PathBuf::from("var/")).expect("to succeed");
|
#[test]
|
||||||
|
fn it_deletes_a_file() {
|
||||||
|
let tmp = TempDir::new("var").unwrap();
|
||||||
|
let f =
|
||||||
|
FileHandle::new("rawr.png".to_owned(), PathBuf::from(tmp.path())).expect("to succeed");
|
||||||
|
f.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_can_return_a_thumbnail() {
|
fn it_can_return_a_thumbnail() {
|
||||||
let f = FileHandle::new("rawr.png".to_owned(), PathBuf::from("var/")).expect("to succeed");
|
let tmp = TempDir::new("var").unwrap();
|
||||||
|
let _ =
|
||||||
|
FileHandle::new("rawr.png".to_owned(), PathBuf::from(tmp.path())).expect("to succeed");
|
||||||
/*
|
/*
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
f.thumbnail(),
|
f.thumbnail(),
|
||||||
|
@ -279,14 +265,16 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_can_return_a_file_stream() {
|
fn it_can_return_a_file_stream() {
|
||||||
let f = FileHandle::new("rawr.png".to_owned(), PathBuf::from("var/")).expect("to succeed");
|
let tmp = TempDir::new("var").unwrap();
|
||||||
|
let _ =
|
||||||
|
FileHandle::new("rawr.png".to_owned(), PathBuf::from(tmp.path())).expect("to succeed");
|
||||||
// f.stream().expect("to succeed");
|
// f.stream().expect("to succeed");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_raises_an_error_when_file_not_found() {
|
fn it_raises_an_error_when_file_not_found() {
|
||||||
let resolver = PathResolver::try_from("var/rawr.png").expect("a valid path");
|
let tmp = TempDir::new("var").unwrap();
|
||||||
match FileHandle::load(&FileId::from("rawr"), &PathBuf::from("var/")) {
|
match FileHandle::load(&FileId::from("rawr"), tmp.path()) {
|
||||||
Err(ReadFileError::FileNotFound(_)) => assert!(true),
|
Err(ReadFileError::FileNotFound(_)) => assert!(true),
|
||||||
_ => assert!(false),
|
_ => assert!(false),
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,15 +41,12 @@ impl FileInfo {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::store::{filehandle::PathResolver, utils::FileCleanup, FileId};
|
use crate::store::FileId;
|
||||||
use std::convert::TryFrom;
|
use tempdir::TempDir;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_saves_and_loads_metadata() {
|
fn it_saves_and_loads_metadata() {
|
||||||
let resolver = PathResolver::try_from("var/1617654d-a588-4714-b4fa-e00ed0a8a607.png")
|
let tmp = TempDir::new("var").unwrap();
|
||||||
.expect("a valid path");
|
|
||||||
let _cleanup = FileCleanup(resolver.metadata_path());
|
|
||||||
|
|
||||||
let created = Utc::now();
|
let created = Utc::now();
|
||||||
|
|
||||||
let info = FileInfo {
|
let info = FileInfo {
|
||||||
|
@ -60,9 +57,11 @@ mod test {
|
||||||
hash: "abcdefg".to_owned(),
|
hash: "abcdefg".to_owned(),
|
||||||
extension: "png".to_owned(),
|
extension: "png".to_owned(),
|
||||||
};
|
};
|
||||||
info.save(resolver.metadata_path()).unwrap();
|
let mut path = tmp.path().to_owned();
|
||||||
|
path.push(&PathBuf::from(info.id.clone()));
|
||||||
|
info.save(path.clone()).unwrap();
|
||||||
|
|
||||||
let info_ = FileInfo::load(resolver.metadata_path()).unwrap();
|
let info_ = FileInfo::load(path).unwrap();
|
||||||
assert_eq!(info_.size, 23777);
|
assert_eq!(info_.size, 23777);
|
||||||
assert_eq!(info_.created, info.created);
|
assert_eq!(info_.created, info.created);
|
||||||
assert_eq!(info_.file_type, "image/png");
|
assert_eq!(info_.file_type, "image/png");
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::collections::HashSet;
|
||||||
ops::Deref,
|
use std::{ops::Deref, path::PathBuf};
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
mod filehandle;
|
mod filehandle;
|
||||||
mod fileinfo;
|
mod fileinfo;
|
||||||
mod thumbnail;
|
|
||||||
pub mod utils;
|
|
||||||
|
|
||||||
pub use filehandle::FileHandle;
|
pub use filehandle::FileHandle;
|
||||||
pub use fileinfo::FileInfo;
|
pub use fileinfo::FileInfo;
|
||||||
pub use thumbnail::Thumbnail;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum WriteFileError {
|
pub enum WriteFileError {
|
||||||
|
@ -118,8 +113,21 @@ impl Store {
|
||||||
Self { files_root }
|
Self { files_root }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_files(&self) -> Result<Vec<FileId>, ReadFileError> {
|
pub fn list_files(&self) -> Result<HashSet<FileId>, ReadFileError> {
|
||||||
unimplemented!()
|
let paths = std::fs::read_dir(&self.files_root)?;
|
||||||
|
let info_files = paths
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|path| {
|
||||||
|
let path_ = path.unwrap().path();
|
||||||
|
if path_.extension().and_then(|s| s.to_str()) == Some("json") {
|
||||||
|
let stem = path_.file_stem().and_then(|s| s.to_str()).unwrap();
|
||||||
|
Some(FileId::from(FileId::from(stem)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<HashSet<FileId>>();
|
||||||
|
Ok(info_files)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_file(
|
pub fn add_file(
|
||||||
|
@ -153,50 +161,49 @@ impl Store {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::store::utils::FileCleanup;
|
|
||||||
use cool_asserts::assert_matches;
|
use cool_asserts::assert_matches;
|
||||||
use std::{collections::HashSet, io::Read};
|
use std::{collections::HashSet, io::Read};
|
||||||
|
use tempdir::TempDir;
|
||||||
|
|
||||||
fn with_file<F>(test_fn: F)
|
fn with_file<F>(test_fn: F)
|
||||||
where
|
where
|
||||||
F: FnOnce(Store, FileId),
|
F: FnOnce(Store, FileId, TempDir),
|
||||||
{
|
{
|
||||||
|
let tmp = TempDir::new("var").unwrap();
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let mut file = std::fs::File::open("fixtures/rawr.png").unwrap();
|
let mut file = std::fs::File::open("fixtures/rawr.png").unwrap();
|
||||||
file.read_to_end(&mut buf).unwrap();
|
file.read_to_end(&mut buf).unwrap();
|
||||||
|
|
||||||
let mut store = Store::new(PathBuf::from("var/"));
|
let mut store = Store::new(PathBuf::from(tmp.path()));
|
||||||
let file_record = store.add_file("rawr.png".to_owned(), buf).unwrap();
|
let file_record = store.add_file("rawr.png".to_owned(), buf).unwrap();
|
||||||
|
|
||||||
let _file = FileCleanup(PathBuf::from(format!("var/{}.png", *file_record.id)));
|
test_fn(store, file_record.id, tmp);
|
||||||
let _md = FileCleanup(PathBuf::from(format!("var/{}.json", *file_record.id)));
|
|
||||||
let _tn = FileCleanup(PathBuf::from(format!("var/{}.tn.png", *file_record.id)));
|
|
||||||
|
|
||||||
test_fn(store, file_record.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn adds_files() {
|
fn adds_files() {
|
||||||
with_file(|store, id| {
|
with_file(|store, id, tmp| {
|
||||||
let file = store.get_file(&id).expect("to retrieve the file");
|
let file = store.get_file(&id).expect("to retrieve the file");
|
||||||
|
|
||||||
assert_eq!(file.content().map(|file| file.len()).unwrap(), 23777);
|
assert_eq!(file.content().map(|file| file.len()).unwrap(), 23777);
|
||||||
|
|
||||||
assert!(PathBuf::from(format!("var/{}.png", *id)).exists());
|
assert!(tmp.path().join(&(*id)).with_extension("png").exists());
|
||||||
assert!(PathBuf::from(format!("var/{}.json", *id)).exists());
|
assert!(tmp.path().join(&(*id)).with_extension("json").exists());
|
||||||
assert!(PathBuf::from(format!("var/{}.tn.png", *id)).exists());
|
assert!(tmp.path().join(&(*id)).with_extension("tn.png").exists());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sets_up_metadata_for_file() {
|
fn sets_up_metadata_for_file() {
|
||||||
with_file(|store, id| {
|
with_file(|store, id, tmp| {
|
||||||
|
assert!(tmp.path().join(&(*id)).with_extension("png").exists());
|
||||||
let info = store.get_metadata(&id).expect("to retrieve the metadata");
|
let info = store.get_metadata(&id).expect("to retrieve the metadata");
|
||||||
|
|
||||||
assert_matches!(info, FileInfo { size, file_type, hash, extension, .. } => {
|
assert_matches!(info, FileInfo { size, file_type, hash, extension, .. } => {
|
||||||
assert_eq!(size, 23777);
|
assert_eq!(size, 23777);
|
||||||
assert_eq!(file_type, "image/png");
|
assert_eq!(file_type, "image/png");
|
||||||
assert_eq!(hash, "".to_owned());
|
assert_eq!(hash, "b6cd35e113b95d62f53d9cbd27ccefef47d3e324aef01a2db6c0c6d3a43c89ee".to_owned());
|
||||||
assert_eq!(extension, "png".to_owned());
|
assert_eq!(extension, "png".to_owned());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -214,22 +221,23 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deletes_associated_files() {
|
fn deletes_associated_files() {
|
||||||
with_file(|mut store, id| {
|
with_file(|mut store, id, tmp| {
|
||||||
store.delete_file(&id).expect("file to be deleted");
|
store.delete_file(&id).expect("file to be deleted");
|
||||||
|
|
||||||
assert!(!PathBuf::from(format!("var/{}.png", *id)).exists());
|
assert!(!tmp.path().join(&(*id)).with_extension("png").exists());
|
||||||
assert!(!PathBuf::from(format!("var/{}.json", *id)).exists());
|
assert!(!tmp.path().join(&(*id)).with_extension("json").exists());
|
||||||
assert!(!PathBuf::from(format!("var/{}.tn.png", *id)).exists());
|
assert!(!tmp.path().join(&(*id)).with_extension("tn.png").exists());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lists_files_in_the_db() {
|
fn lists_files_in_the_db() {
|
||||||
with_file(|store, id| {
|
with_file(|store, id, _| {
|
||||||
let resolvers = store.list_files().expect("file listing to succeed");
|
let resolvers = store.list_files().expect("file listing to succeed");
|
||||||
let ids = resolvers.into_iter().collect::<HashSet<FileId>>();
|
let ids = resolvers.into_iter().collect::<HashSet<FileId>>();
|
||||||
|
|
||||||
assert_eq!(ids.len(), 1);
|
assert_eq!(ids.len(), 1);
|
||||||
|
assert!(ids.contains(&id));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
pub struct FileCleanup(pub PathBuf);
|
|
||||||
|
|
||||||
impl Drop for FileCleanup {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = std::fs::remove_file(&self.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn append_extension(path: &Path, extra_ext: &str) -> PathBuf {
|
|
||||||
let ext_ = match path.extension() {
|
|
||||||
None => String::from(extra_ext),
|
|
||||||
Some(ext) => [ext.to_string_lossy(), std::borrow::Cow::from(extra_ext)].join("."),
|
|
||||||
};
|
|
||||||
path.with_extension(ext_)
|
|
||||||
}
|
|
Loading…
Reference in New Issue