diff --git a/src/domain/config.rs b/src/domain/config.rs index b66b288..365f70a 100644 --- a/src/domain/config.rs +++ b/src/domain/config.rs @@ -28,24 +28,24 @@ pub enum SetError { Unexpected(Box), } -#[automock] +#[mockall::automock] pub trait Store { fn get(&self, domain: &str) -> Result; fn set(&self, domain: &str, config: &Config) -> Result<(), SetError>; } -pub struct FSStore { +struct FSStore { dir_path: PathBuf, } -impl FSStore { - pub fn new(dir_path: &Path) -> io::Result { - fs::create_dir_all(dir_path)?; - Ok(FSStore { - dir_path: dir_path.into(), - }) - } +pub fn new_store(dir_path: &Path) -> io::Result { + fs::create_dir_all(dir_path)?; + Ok(FSStore { + dir_path: dir_path.into(), + }) +} +impl FSStore { fn config_dir_path(&self, domain: &str) -> PathBuf { self.dir_path.join(domain) } @@ -90,7 +90,7 @@ mod tests { fn basic() { let tmp_dir = TempDir::new("domain_config_store").unwrap(); - let store = FSStore::new(tmp_dir.path()).expect("store created"); + let store = new_store(tmp_dir.path()).expect("store created"); let domain = "foo"; diff --git a/src/origin.rs b/src/origin.rs index eee78f4..5245190 100644 --- a/src/origin.rs +++ b/src/origin.rs @@ -1,3 +1,26 @@ +use std::error::Error; + mod descr; pub use self::descr::Descr; pub mod store; + +#[derive(thiserror::Error, Debug)] +pub enum ReadFileIntoError { + #[error("file not found")] + FileNotFound, + + #[error(transparent)] + Unexpected(Box), +} + +#[mockall::automock] +/// Describes an origin which has already been synced locally and is available for reading files +/// from. +pub trait Origin { + fn descr(&self) -> &Descr; + fn read_file_into( + &self, + path: &str, + into: &mut dyn std::io::Write, + ) -> Result<(), ReadFileIntoError>; +} diff --git a/src/origin/store.rs b/src/origin/store.rs index 02156cb..9df3e4c 100644 --- a/src/origin/store.rs +++ b/src/origin/store.rs @@ -1,5 +1,4 @@ -use super::Descr; -use mockall::automock; +use crate::origin; use std::error::Error; #[derive(Clone, Copy)] @@ -7,15 +6,6 @@ pub struct Limits { // TODO storage limits } -#[derive(thiserror::Error, Debug)] -pub enum ReadFileIntoError { - #[error("file not found")] - FileNotFound, - - #[error(transparent)] - Unexpected(Box), -} - #[derive(thiserror::Error, Debug)] pub enum SyncError { #[error("invalid url")] @@ -43,36 +33,26 @@ pub enum AllDescrsError { Unexpected(Box), } -#[automock] -pub trait Origin { - fn descr(&self) -> &Descr; - fn read_file_into( - &self, - path: &str, - into: &mut dyn std::io::Write, - ) -> Result<(), ReadFileIntoError>; -} - /// Used in the return from all_descrs from Store. pub type AllDescrsResult = Result; /// Describes a storage mechanism for Origins. Each Origin is uniquely identified by its Descr. pub trait Store<'a> { - type AllDescrsIter: IntoIterator>; + type AllDescrsIter: IntoIterator>; /// If the origin is of a kind which can be updated, sync will pull down the latest version of /// the origin into the storage. - fn sync(&'a self, descr: &Descr, limits: Limits) -> Result<(), SyncError>; + fn sync(&'a self, descr: &origin::Descr, limits: Limits) -> Result<(), SyncError>; - fn get(&'a self, descr: Descr) -> Result, GetError>; + fn get(&'a self, descr: origin::Descr) -> Result, GetError>; fn all_descrs(&'a self) -> AllDescrsResult; } pub struct MockStore where - SyncFn: Fn(&Descr, Limits) -> Result<(), SyncError>, - GetFn: Fn(Descr) -> Result, GetError>, - AllDescrsFn: Fn() -> AllDescrsResult>>, + SyncFn: Fn(&origin::Descr, Limits) -> Result<(), SyncError>, + GetFn: Fn(origin::Descr) -> Result, GetError>, + AllDescrsFn: Fn() -> AllDescrsResult>>, { pub sync_fn: SyncFn, pub get_fn: GetFn, @@ -81,17 +61,17 @@ where impl<'a, SyncFn, GetFn, AllDescrsFn> Store<'a> for MockStore where - SyncFn: Fn(&Descr, Limits) -> Result<(), SyncError>, - GetFn: Fn(Descr) -> Result, GetError>, - AllDescrsFn: Fn() -> AllDescrsResult>>, + SyncFn: Fn(&origin::Descr, Limits) -> Result<(), SyncError>, + GetFn: Fn(origin::Descr) -> Result, GetError>, + AllDescrsFn: Fn() -> AllDescrsResult>>, { - type AllDescrsIter = Vec>; + type AllDescrsIter = Vec>; - fn sync(&'a self, descr: &Descr, limits: Limits) -> Result<(), SyncError> { + fn sync(&'a self, descr: &origin::Descr, limits: Limits) -> Result<(), SyncError> { (self.sync_fn)(descr, limits) } - fn get(&'a self, descr: Descr) -> Result, GetError> { + fn get(&'a self, descr: origin::Descr) -> Result, GetError> { (self.get_fn)(descr) } @@ -102,18 +82,20 @@ where pub mod git { - use super::*; + use crate::origin; + use crate::origin::store; + use std::path::{Path, PathBuf}; use std::{fs, io}; struct Origin { - descr: Descr, + descr: origin::Descr, repo: gix::Repository, tree_object_id: gix::ObjectId, } - impl super::Origin for Origin { - fn descr(&self) -> &Descr { + impl origin::Origin for Origin { + fn descr(&self) -> &origin::Descr { &self.descr } @@ -121,24 +103,24 @@ pub mod git { &self, path: &str, into: &mut dyn std::io::Write, - ) -> Result<(), ReadFileIntoError> { + ) -> Result<(), origin::ReadFileIntoError> { let mut clean_path = Path::new(path); clean_path = clean_path.strip_prefix("/").unwrap_or(clean_path); let file_object = self .repo .find_object(self.tree_object_id) - .map_err(|e| ReadFileIntoError::Unexpected(Box::from(e)))? + .map_err(|e| origin::ReadFileIntoError::Unexpected(Box::from(e)))? .peel_to_tree() - .map_err(|e| ReadFileIntoError::Unexpected(Box::from(e)))? + .map_err(|e| origin::ReadFileIntoError::Unexpected(Box::from(e)))? .lookup_entry_by_path(clean_path) - .map_err(|e| ReadFileIntoError::Unexpected(Box::from(e)))? - .ok_or(ReadFileIntoError::FileNotFound)? + .map_err(|e| origin::ReadFileIntoError::Unexpected(Box::from(e)))? + .ok_or(origin::ReadFileIntoError::FileNotFound)? .object() - .map_err(|e| ReadFileIntoError::Unexpected(Box::from(e)))?; + .map_err(|e| origin::ReadFileIntoError::Unexpected(Box::from(e)))?; into.write_all(file_object.data.as_ref()) - .map_err(|e| ReadFileIntoError::Unexpected(Box::from(e)))?; + .map_err(|e| origin::ReadFileIntoError::Unexpected(Box::from(e)))?; Ok(()) } @@ -156,7 +138,7 @@ pub mod git { Ok(Store { dir_path }) } - fn repo_path(&self, descr: &Descr) -> PathBuf { + fn repo_path(&self, descr: &origin::Descr) -> PathBuf { self.dir_path.join(descr.id()) } @@ -170,9 +152,13 @@ pub mod git { } impl<'a> super::Store<'a> for Store { - type AllDescrsIter = Box> + 'a>; + type AllDescrsIter = Box> + 'a>; - fn sync(&'a self, descr: &Descr, _limits: Limits) -> Result<(), SyncError> { + fn sync( + &'a self, + descr: &origin::Descr, + _limits: store::Limits, + ) -> Result<(), store::SyncError> { use gix::clone::Error as gixCloneErr; use gix::progress::Discard; @@ -182,85 +168,88 @@ pub mod git { // if the path doesn't exist then use the gix clone feature to clone it into the // directory. if fs::read_dir(repo_path).is_err() { - fs::create_dir_all(repo_path).map_err(|e| SyncError::Unexpected(Box::from(e)))?; + fs::create_dir_all(repo_path) + .map_err(|e| store::SyncError::Unexpected(Box::from(e)))?; - let Descr::Git { url, branch_name } = descr; + let origin::Descr::Git { url, branch_name } = descr; let (repo, _) = gix::prepare_clone_bare(url.clone(), repo_path) .map_err(|e| match e { gixCloneErr::Init(gix::init::Error::InvalidBranchName { .. }) => { - SyncError::InvalidBranchName + store::SyncError::InvalidBranchName } gixCloneErr::UrlParse(_) | gixCloneErr::CanonicalizeUrl { .. } => { - SyncError::InvalidURL + store::SyncError::InvalidURL } - _ => SyncError::Unexpected(Box::from(e)), + _ => store::SyncError::Unexpected(Box::from(e)), })? .fetch_only(Discard, should_interrupt) - .map_err(|_| SyncError::InvalidURL)?; + .map_err(|_| store::SyncError::InvalidURL)?; // Check to make sure the branch name exists // TODO if this fails we should delete repo_path repo.try_find_reference(&self.branch_ref(branch_name)) - .map_err(|e| SyncError::Unexpected(Box::from(e)))? - .ok_or(SyncError::InvalidBranchName)?; + .map_err(|e| store::SyncError::Unexpected(Box::from(e)))? + .ok_or(store::SyncError::InvalidBranchName)?; // Add the descr to the repo directory, so we can know the actual descr later // TODO if this fails we should delete repo_path let descr_file = fs::File::create(self.descr_file_path(descr.id().as_ref())) - .map_err(|e| SyncError::Unexpected(Box::from(e)))?; + .map_err(|e| store::SyncError::Unexpected(Box::from(e)))?; serde_json::to_writer(descr_file, &descr) - .map_err(|e| SyncError::Unexpected(Box::from(e)))?; + .map_err(|e| store::SyncError::Unexpected(Box::from(e)))?; return Ok(()); } let direction = gix::remote::Direction::Fetch; - let repo = gix::open(repo_path).map_err(|e| SyncError::Unexpected(Box::from(e)))?; + let repo = + gix::open(repo_path).map_err(|e| store::SyncError::Unexpected(Box::from(e)))?; let remote = repo .find_default_remote(direction) - .ok_or_else(|| SyncError::Unexpected(Box::from("no default configured")))? - .map_err(|e| SyncError::Unexpected(Box::from(e)))?; + .ok_or_else(|| store::SyncError::Unexpected(Box::from("no default configured")))? + .map_err(|e| store::SyncError::Unexpected(Box::from(e)))?; remote .connect(direction) - .map_err(|e| SyncError::Unexpected(Box::from(e)))? + .map_err(|e| store::SyncError::Unexpected(Box::from(e)))? .prepare_fetch(Discard, Default::default()) - .map_err(|e| SyncError::Unexpected(Box::from(e)))? + .map_err(|e| store::SyncError::Unexpected(Box::from(e)))? .receive(Discard, should_interrupt) - .map_err(|e| SyncError::Unexpected(Box::from(e)))?; + .map_err(|e| store::SyncError::Unexpected(Box::from(e)))?; Ok(()) } - fn get(&'a self, descr: Descr) -> Result, GetError> { + fn get(&'a self, descr: origin::Descr) -> Result, store::GetError> { let repo_path = self.repo_path(&descr); - let Descr::Git { + let origin::Descr::Git { ref branch_name, .. } = descr; fs::read_dir(&repo_path).map_err(|e| match e.kind() { - io::ErrorKind::NotFound => GetError::NotFound, - _ => GetError::Unexpected(Box::from(e)), + io::ErrorKind::NotFound => store::GetError::NotFound, + _ => store::GetError::Unexpected(Box::from(e)), })?; - let repo = gix::open(&repo_path).map_err(|e| GetError::Unexpected(Box::from(e)))?; + let repo = + gix::open(&repo_path).map_err(|e| store::GetError::Unexpected(Box::from(e)))?; let commit_object_id = repo .find_reference(&self.branch_ref(branch_name)) - .map_err(|e| GetError::Unexpected(Box::from(e)))? + .map_err(|e| store::GetError::Unexpected(Box::from(e)))? .peel_to_id_in_place() - .map_err(|e| GetError::Unexpected(Box::from(e)))? + .map_err(|e| store::GetError::Unexpected(Box::from(e)))? .detach(); let tree_object_id = repo .find_object(commit_object_id) - .map_err(|e| GetError::Unexpected(Box::from(e)))? + .map_err(|e| store::GetError::Unexpected(Box::from(e)))? .try_to_commit_ref() - .map_err(|e| GetError::Unexpected(Box::from(e)))? + .map_err(|e| store::GetError::Unexpected(Box::from(e)))? .tree(); return Ok(Box::from(Origin { @@ -270,18 +259,18 @@ pub mod git { })); } - fn all_descrs(&'a self) -> AllDescrsResult { + fn all_descrs(&'a self) -> store::AllDescrsResult { Ok(Box::from( fs::read_dir(&self.dir_path) - .map_err(|e| AllDescrsError::Unexpected(Box::from(e)))? + .map_err(|e| store::AllDescrsError::Unexpected(Box::from(e)))? .map( - |dir_entry_res: io::Result| -> AllDescrsResult { + |dir_entry_res: io::Result| -> store::AllDescrsResult { let descr_id: String = dir_entry_res - .map_err(|e| AllDescrsError::Unexpected(Box::from(e)))? + .map_err(|e| store::AllDescrsError::Unexpected(Box::from(e)))? .file_name() .to_str() .ok_or_else(|| { - AllDescrsError::Unexpected(Box::from( + store::AllDescrsError::Unexpected(Box::from( "couldn't convert os string to &str", )) })? @@ -290,10 +279,10 @@ pub mod git { let descr_file_path = self.descr_file_path(descr_id.as_ref()); let descr_file = fs::File::open(descr_file_path) - .map_err(|e| AllDescrsError::Unexpected(Box::from(e)))?; + .map_err(|e| store::AllDescrsError::Unexpected(Box::from(e)))?; let descr = serde_json::from_reader(descr_file) - .map_err(|e| AllDescrsError::Unexpected(Box::from(e)))?; + .map_err(|e| store::AllDescrsError::Unexpected(Box::from(e)))?; Ok(descr) }, @@ -304,8 +293,9 @@ pub mod git { #[cfg(test)] mod tests { - use super::super::{Origin, Store}; - use super::Descr; + use crate::origin; + use crate::origin::store; + use crate::origin::store::Store; use tempdir::TempDir; #[test] @@ -314,17 +304,17 @@ pub mod git { let curr_dir = format!("file://{}", std::env::current_dir().unwrap().display()); - let descr = Descr::Git { + let descr = origin::Descr::Git { url: curr_dir.clone(), branch_name: String::from("master"), }; - let other_descr = Descr::Git { + let other_descr = origin::Descr::Git { url: curr_dir.clone(), branch_name: String::from("some_other_branch"), }; - let limits = super::Limits {}; + let limits = store::Limits {}; let store = super::Store::new(tmp_dir.path().to_path_buf()).expect("store created"); @@ -335,7 +325,7 @@ pub mod git { assert!(matches!( store.get(other_descr), - Err::, super::GetError>(super::GetError::NotFound), + Err::, store::GetError>(store::GetError::NotFound), )); let origin = store.get(descr.clone()).expect("origin retrieved"); @@ -357,14 +347,14 @@ pub mod git { let mut into: Vec = vec![]; assert!(matches!( origin.read_file_into("DNE", &mut into), - Err::<(), super::ReadFileIntoError>(super::ReadFileIntoError::FileNotFound), + Err::<(), origin::ReadFileIntoError>(origin::ReadFileIntoError::FileNotFound), )); assert_eq!(into.len(), 0); let descrs = store .all_descrs() .expect("all_descrs callsed") - .collect::>>(); + .collect::>>(); assert_eq!(1, descrs.len()); assert_eq!(&descr, descrs[0].as_ref().unwrap());