re-organize imports for clarity

This commit is contained in:
Brian Picciano 2023-05-09 16:37:06 +02:00
parent a8c96f9a2e
commit f2a3c78a83
3 changed files with 110 additions and 97 deletions

View File

@ -28,24 +28,24 @@ pub enum SetError {
Unexpected(Box<dyn Error>),
}
#[automock]
#[mockall::automock]
pub trait Store {
fn get(&self, domain: &str) -> Result<Config, GetError>;
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<FSStore> {
fs::create_dir_all(dir_path)?;
Ok(FSStore {
dir_path: dir_path.into(),
})
}
pub fn new_store(dir_path: &Path) -> io::Result<impl Store> {
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";

View File

@ -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<dyn Error>),
}
#[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>;
}

View File

@ -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<dyn Error>),
}
#[derive(thiserror::Error, Debug)]
pub enum SyncError {
#[error("invalid url")]
@ -43,36 +33,26 @@ pub enum AllDescrsError {
Unexpected(Box<dyn Error>),
}
#[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<T> = Result<T, AllDescrsError>;
/// Describes a storage mechanism for Origins. Each Origin is uniquely identified by its Descr.
pub trait Store<'a> {
type AllDescrsIter: IntoIterator<Item = AllDescrsResult<Descr>>;
type AllDescrsIter: IntoIterator<Item = AllDescrsResult<origin::Descr>>;
/// 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<Box<dyn Origin>, GetError>;
fn get(&'a self, descr: origin::Descr) -> Result<Box<dyn origin::Origin>, GetError>;
fn all_descrs(&'a self) -> AllDescrsResult<Self::AllDescrsIter>;
}
pub struct MockStore<SyncFn, GetFn, AllDescrsFn>
where
SyncFn: Fn(&Descr, Limits) -> Result<(), SyncError>,
GetFn: Fn(Descr) -> Result<Box<dyn Origin>, GetError>,
AllDescrsFn: Fn() -> AllDescrsResult<Vec<AllDescrsResult<Descr>>>,
SyncFn: Fn(&origin::Descr, Limits) -> Result<(), SyncError>,
GetFn: Fn(origin::Descr) -> Result<Box<dyn origin::Origin>, GetError>,
AllDescrsFn: Fn() -> AllDescrsResult<Vec<AllDescrsResult<origin::Descr>>>,
{
pub sync_fn: SyncFn,
pub get_fn: GetFn,
@ -81,17 +61,17 @@ where
impl<'a, SyncFn, GetFn, AllDescrsFn> Store<'a> for MockStore<SyncFn, GetFn, AllDescrsFn>
where
SyncFn: Fn(&Descr, Limits) -> Result<(), SyncError>,
GetFn: Fn(Descr) -> Result<Box<dyn Origin>, GetError>,
AllDescrsFn: Fn() -> AllDescrsResult<Vec<AllDescrsResult<Descr>>>,
SyncFn: Fn(&origin::Descr, Limits) -> Result<(), SyncError>,
GetFn: Fn(origin::Descr) -> Result<Box<dyn origin::Origin>, GetError>,
AllDescrsFn: Fn() -> AllDescrsResult<Vec<AllDescrsResult<origin::Descr>>>,
{
type AllDescrsIter = Vec<AllDescrsResult<Descr>>;
type AllDescrsIter = Vec<AllDescrsResult<origin::Descr>>;
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<Box<dyn Origin>, GetError> {
fn get(&'a self, descr: origin::Descr) -> Result<Box<dyn origin::Origin>, 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<dyn Iterator<Item = AllDescrsResult<Descr>> + 'a>;
type AllDescrsIter = Box<dyn Iterator<Item = store::AllDescrsResult<origin::Descr>> + '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<Box<dyn super::Origin>, GetError> {
fn get(&'a self, descr: origin::Descr) -> Result<Box<dyn origin::Origin>, 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<Self::AllDescrsIter> {
fn all_descrs(&'a self) -> store::AllDescrsResult<Self::AllDescrsIter> {
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<fs::DirEntry>| -> AllDescrsResult<Descr> {
|dir_entry_res: io::Result<fs::DirEntry>| -> store::AllDescrsResult<origin::Descr> {
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::<Box<dyn Origin>, super::GetError>(super::GetError::NotFound),
Err::<Box<dyn origin::Origin>, 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<u8> = 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::<Vec<Result<Descr, super::AllDescrsError>>>();
.collect::<Vec<Result<origin::Descr, store::AllDescrsError>>>();
assert_eq!(1, descrs.len());
assert_eq!(&descr, descrs[0].as_ref().unwrap());