make origin store errors be actual errors, and implement a mock

This commit is contained in:
Brian Picciano 2023-05-09 13:40:49 +02:00
parent 696c92a292
commit be84742e94
3 changed files with 109 additions and 74 deletions

1
Cargo.lock generated
View File

@ -451,6 +451,7 @@ dependencies = [
"serde_json", "serde_json",
"sha2", "sha2",
"tempdir", "tempdir",
"thiserror",
"trust-dns-client", "trust-dns-client",
] ]

View File

@ -19,3 +19,4 @@ serde = { version = "1.0.162", features = [ "derive" ]}
serde_json = "1.0.96" serde_json = "1.0.96"
trust-dns-client = "0.22.0" trust-dns-client = "0.22.0"
mockall = "0.11.4" mockall = "0.11.4"
thiserror = "1.0.40"

View File

@ -7,54 +7,42 @@ pub struct Limits {
// TODO storage limits // TODO storage limits
} }
#[derive(Debug)] #[derive(thiserror::Error, Debug)]
pub enum ReadFileIntoError { pub enum ReadFileIntoError {
#[error("file not found")]
FileNotFound, FileNotFound,
#[error(transparent)]
Unexpected(Box<dyn Error>), Unexpected(Box<dyn Error>),
} }
impl<E: Error + 'static> From<E> for ReadFileIntoError { #[derive(thiserror::Error, Debug)]
fn from(e: E) -> ReadFileIntoError {
ReadFileIntoError::Unexpected(Box::from(e))
}
}
#[derive(Debug)]
pub enum SyncError { pub enum SyncError {
#[error("invalid url")]
InvalidURL, InvalidURL,
#[error("invalid branch name")]
InvalidBranchName, InvalidBranchName,
#[error(transparent)]
Unexpected(Box<dyn Error>), Unexpected(Box<dyn Error>),
} }
impl<E: Error + 'static> From<E> for SyncError { #[derive(thiserror::Error, Debug)]
fn from(e: E) -> SyncError {
SyncError::Unexpected(Box::from(e))
}
}
#[derive(Debug)]
pub enum GetError { pub enum GetError {
#[error("not found")]
NotFound, NotFound,
#[error(transparent)]
Unexpected(Box<dyn Error>), Unexpected(Box<dyn Error>),
} }
impl<E: Error + 'static> From<E> for GetError { #[derive(thiserror::Error, Debug)]
fn from(e: E) -> GetError {
GetError::Unexpected(Box::from(e))
}
}
#[derive(Debug)]
pub enum AllDescrsError { pub enum AllDescrsError {
#[error(transparent)]
Unexpected(Box<dyn Error>), Unexpected(Box<dyn Error>),
} }
impl<E: Error + 'static> From<E> for AllDescrsError {
fn from(e: E) -> AllDescrsError {
AllDescrsError::Unexpected(Box::from(e))
}
}
#[automock] #[automock]
pub trait Origin { pub trait Origin {
fn descr(&self) -> &Descr; fn descr(&self) -> &Descr;
@ -80,15 +68,37 @@ pub trait Store<'a> {
fn all_descrs(&'a self) -> AllDescrsResult<Self::AllDescrsIter>; fn all_descrs(&'a self) -> AllDescrsResult<Self::AllDescrsIter>;
} }
//mockall::mock! { pub struct MockStore<SyncFn, GetFn, AllDescrsFn>
// pub MyStore {} where
// impl<'a> Store<'a> for MyStore { SyncFn: Fn(&Descr, Limits) -> Result<(), SyncError>,
// type AllDescrsIter = Vec<AllDescrsResult<Descr>>; GetFn: Fn(Descr) -> Result<Box<dyn Origin>, GetError>,
// fn sync(&'a self, descr: &Descr, limits: Limits) -> Result<(), SyncError>; AllDescrsFn: Fn() -> AllDescrsResult<Vec<AllDescrsResult<Descr>>>,
// fn get(&'a self, descr: Descr) -> Result<Box<dyn Origin>, GetError>; {
// fn all_descrs(&'a self) -> AllDescrsResult<<self::MockMyStore as self::Store<'a>>::AllDescrsIter>; pub sync_fn: SyncFn,
// } pub get_fn: GetFn,
//} pub all_descrs_fn: AllDescrsFn,
}
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>>>,
{
type AllDescrsIter = Vec<AllDescrsResult<Descr>>;
fn sync(&'a self, descr: &Descr, limits: Limits) -> Result<(), SyncError> {
(self.sync_fn)(descr, limits)
}
fn get(&'a self, descr: Descr) -> Result<Box<dyn Origin>, GetError> {
(self.get_fn)(descr)
}
fn all_descrs(&'a self) -> AllDescrsResult<Self::AllDescrsIter> {
(self.all_descrs_fn)()
}
}
pub mod git { pub mod git {
@ -117,13 +127,18 @@ pub mod git {
let file_object = self let file_object = self
.repo .repo
.find_object(self.tree_object_id)? .find_object(self.tree_object_id)
.peel_to_tree()? .map_err(|e| ReadFileIntoError::Unexpected(Box::from(e)))?
.lookup_entry_by_path(clean_path)? .peel_to_tree()
.map_err(|e| ReadFileIntoError::Unexpected(Box::from(e)))?
.lookup_entry_by_path(clean_path)
.map_err(|e| ReadFileIntoError::Unexpected(Box::from(e)))?
.ok_or(ReadFileIntoError::FileNotFound)? .ok_or(ReadFileIntoError::FileNotFound)?
.object()?; .object()
.map_err(|e| ReadFileIntoError::Unexpected(Box::from(e)))?;
into.write_all(file_object.data.as_ref())?; into.write_all(file_object.data.as_ref())
.map_err(|e| ReadFileIntoError::Unexpected(Box::from(e)))?;
Ok(()) Ok(())
} }
@ -167,7 +182,7 @@ pub mod git {
// if the path doesn't exist then use the gix clone feature to clone it into the // if the path doesn't exist then use the gix clone feature to clone it into the
// directory. // directory.
if fs::read_dir(repo_path).is_err() { if fs::read_dir(repo_path).is_err() {
fs::create_dir_all(repo_path)?; fs::create_dir_all(repo_path).map_err(|e| SyncError::Unexpected(Box::from(e)))?;
let Descr::Git { url, branch_name } = descr; let Descr::Git { url, branch_name } = descr;
@ -186,30 +201,37 @@ pub mod git {
// Check to make sure the branch name exists // Check to make sure the branch name exists
// TODO if this fails we should delete repo_path // TODO if this fails we should delete repo_path
repo.try_find_reference(&self.branch_ref(branch_name))? repo.try_find_reference(&self.branch_ref(branch_name))
.map_err(|e| SyncError::Unexpected(Box::from(e)))?
.ok_or(SyncError::InvalidBranchName)?; .ok_or(SyncError::InvalidBranchName)?;
// Add the descr to the repo directory, so we can know the actual descr later // Add the descr to the repo directory, so we can know the actual descr later
// TODO if this fails we should delete repo_path // TODO if this fails we should delete repo_path
let descr_file = fs::File::create(self.descr_file_path(descr.id().as_ref()))?; let descr_file = fs::File::create(self.descr_file_path(descr.id().as_ref()))
.map_err(|e| SyncError::Unexpected(Box::from(e)))?;
serde_json::to_writer(descr_file, &descr)?; serde_json::to_writer(descr_file, &descr)
.map_err(|e| SyncError::Unexpected(Box::from(e)))?;
return Ok(()); return Ok(());
} }
let direction = gix::remote::Direction::Fetch; let direction = gix::remote::Direction::Fetch;
let repo = gix::open(repo_path)?; let repo = gix::open(repo_path).map_err(|e| SyncError::Unexpected(Box::from(e)))?;
let remote = repo let remote = repo
.find_default_remote(direction) .find_default_remote(direction)
.ok_or_else(|| SyncError::Unexpected(Box::from("no default configured")))??; .ok_or_else(|| SyncError::Unexpected(Box::from("no default configured")))?
.map_err(|e| SyncError::Unexpected(Box::from(e)))?;
remote remote
.connect(direction)? .connect(direction)
.prepare_fetch(Discard, Default::default())? .map_err(|e| SyncError::Unexpected(Box::from(e)))?
.receive(Discard, should_interrupt)?; .prepare_fetch(Discard, Default::default())
.map_err(|e| SyncError::Unexpected(Box::from(e)))?
.receive(Discard, should_interrupt)
.map_err(|e| SyncError::Unexpected(Box::from(e)))?;
Ok(()) Ok(())
} }
@ -222,19 +244,23 @@ pub mod git {
fs::read_dir(&repo_path).map_err(|e| match e.kind() { fs::read_dir(&repo_path).map_err(|e| match e.kind() {
io::ErrorKind::NotFound => GetError::NotFound, io::ErrorKind::NotFound => GetError::NotFound,
_ => e.into(), _ => GetError::Unexpected(Box::from(e)),
})?; })?;
let repo = gix::open(&repo_path)?; let repo = gix::open(&repo_path).map_err(|e| GetError::Unexpected(Box::from(e)))?;
let commit_object_id = repo let commit_object_id = repo
.find_reference(&self.branch_ref(branch_name))? .find_reference(&self.branch_ref(branch_name))
.peel_to_id_in_place()? .map_err(|e| GetError::Unexpected(Box::from(e)))?
.peel_to_id_in_place()
.map_err(|e| GetError::Unexpected(Box::from(e)))?
.detach(); .detach();
let tree_object_id = repo let tree_object_id = repo
.find_object(commit_object_id)? .find_object(commit_object_id)
.try_to_commit_ref()? .map_err(|e| GetError::Unexpected(Box::from(e)))?
.try_to_commit_ref()
.map_err(|e| GetError::Unexpected(Box::from(e)))?
.tree(); .tree();
return Ok(Box::from(Origin { return Ok(Box::from(Origin {
@ -245,27 +271,34 @@ pub mod git {
} }
fn all_descrs(&'a self) -> AllDescrsResult<Self::AllDescrsIter> { fn all_descrs(&'a self) -> AllDescrsResult<Self::AllDescrsIter> {
Ok(Box::from(fs::read_dir(&self.dir_path)?.map( Ok(Box::from(
|dir_entry_res: io::Result<fs::DirEntry>| -> AllDescrsResult<Descr> { fs::read_dir(&self.dir_path)
let descr_id: String = dir_entry_res? .map_err(|e| AllDescrsError::Unexpected(Box::from(e)))?
.file_name() .map(
.to_str() |dir_entry_res: io::Result<fs::DirEntry>| -> AllDescrsResult<Descr> {
.ok_or_else(|| { let descr_id: String = dir_entry_res
AllDescrsError::Unexpected(Box::from( .map_err(|e| AllDescrsError::Unexpected(Box::from(e)))?
"couldn't convert os string to &str", .file_name()
)) .to_str()
})? .ok_or_else(|| {
.into(); AllDescrsError::Unexpected(Box::from(
"couldn't convert os string to &str",
))
})?
.into();
let descr_file_path = self.descr_file_path(descr_id.as_ref()); let descr_file_path = self.descr_file_path(descr_id.as_ref());
let descr_file = fs::File::open(descr_file_path)?; let descr_file = fs::File::open(descr_file_path)
.map_err(|e| AllDescrsError::Unexpected(Box::from(e)))?;
let descr = serde_json::from_reader(descr_file)?; let descr = serde_json::from_reader(descr_file)
.map_err(|e| AllDescrsError::Unexpected(Box::from(e)))?;
Ok(descr) Ok(descr)
}, },
))) ),
))
} }
} }