use crate::FileId; use super::{ReadFileError, WriteFileError}; use chrono::prelude::*; use serde::{Deserialize, Serialize}; use std::{ io::{Read, Write}, path::PathBuf, }; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct FileInfo { pub id: FileId, // Early versions of the application didn't support a name field, so it is possible that // metadata won't contain the name. We can just default to an empty string when loading the // metadata, as all future versions will require a filename when the file gets uploaded. #[serde(default)] pub name: String, pub size: usize, pub created: DateTime, pub file_type: String, pub hash: String, pub extension: String, } impl FileInfo { pub fn load(path: PathBuf) -> Result { let mut content: Vec = Vec::new(); let mut file = std::fs::File::open(path.clone()).map_err(|_| ReadFileError::FileNotFound(path))?; file.read_to_end(&mut content)?; let js = serde_json::from_slice(&content)?; Ok(js) } pub fn save(&self, path: PathBuf) -> Result<(), WriteFileError> { let ser = serde_json::to_string(self).unwrap(); let mut file = std::fs::File::create(path)?; let _ = file.write(ser.as_bytes())?; Ok(()) } } #[cfg(test)] mod test { use super::*; use crate::store::FileId; use tempdir::TempDir; #[test] fn it_saves_and_loads_metadata() { let tmp = TempDir::new("var").unwrap(); let created = Utc::now(); let info = FileInfo { id: FileId("temp-id".to_owned()), name: "test-image".to_owned(), size: 23777, created, file_type: "image/png".to_owned(), hash: "abcdefg".to_owned(), extension: "png".to_owned(), }; let mut path = tmp.path().to_owned(); path.push(&PathBuf::from(info.id.clone())); info.save(path.clone()).unwrap(); let info_ = FileInfo::load(path).unwrap(); assert_eq!(info_.size, 23777); assert_eq!(info_.created, info.created); assert_eq!(info_.file_type, "image/png"); assert_eq!(info_.hash, info.hash); } }