refactor Store to return an Origin
This commit is contained in:
parent
563f072e09
commit
53e253b362
@ -2,17 +2,19 @@ use super::Descr;
|
||||
use std::error::Error;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Limits {}
|
||||
pub struct Limits {
|
||||
// TODO storage limits
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WriteFileError {
|
||||
pub enum ReadFileIntoError {
|
||||
FileNotFound,
|
||||
Unexpected(Box<dyn Error>),
|
||||
}
|
||||
|
||||
impl<E: Error + 'static> From<E> for WriteFileError {
|
||||
fn from(e: E) -> WriteFileError {
|
||||
WriteFileError::Unexpected(Box::from(e))
|
||||
impl<E: Error + 'static> From<E> for ReadFileIntoError {
|
||||
fn from(e: E) -> ReadFileIntoError {
|
||||
ReadFileIntoError::Unexpected(Box::from(e))
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +31,18 @@ impl<E: Error + 'static> From<E> for SyncError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GetError {
|
||||
NotFound,
|
||||
Unexpected(Box<dyn Error>),
|
||||
}
|
||||
|
||||
impl<E: Error + 'static> From<E> for GetError {
|
||||
fn from(e: E) -> GetError {
|
||||
GetError::Unexpected(Box::from(e))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AllDescrsError {
|
||||
Unexpected(Box<dyn Error>),
|
||||
@ -40,20 +54,25 @@ impl<E: Error + 'static> From<E> for AllDescrsError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Used in the return from all_descrs from Store.
|
||||
type AllDescrsResult<T> = Result<T, AllDescrsError>;
|
||||
|
||||
pub trait Store {
|
||||
fn read_file_into<W>(
|
||||
pub trait Origin {
|
||||
fn descr(&self) -> &Descr;
|
||||
fn read_file_into(
|
||||
&self,
|
||||
descr: &Descr,
|
||||
path: &str,
|
||||
into: &mut W,
|
||||
) -> Result<(), WriteFileError>
|
||||
where
|
||||
W: std::io::Write + ?Sized;
|
||||
into: &mut dyn std::io::Write,
|
||||
) -> Result<(), ReadFileIntoError>;
|
||||
}
|
||||
|
||||
/// Describes a storage mechanism for Origins. Each Origin is uniquely identified by its Descr.
|
||||
pub trait Store {
|
||||
/// 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(&self, descr: &Descr, limits: Limits) -> Result<(), SyncError>;
|
||||
|
||||
fn get(&self, descr: Descr) -> Result<Box<dyn Origin>, GetError>;
|
||||
fn all_descrs(&self) -> AllDescrsResult<Box<dyn Iterator<Item = AllDescrsResult<Descr>> + '_>>;
|
||||
}
|
||||
|
||||
@ -63,6 +82,41 @@ pub mod git {
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{fs, io};
|
||||
|
||||
struct Origin {
|
||||
descr: Descr,
|
||||
repo: gix::Repository,
|
||||
tree_object_id: gix::ObjectId,
|
||||
}
|
||||
|
||||
impl super::Origin for Origin {
|
||||
fn descr(&self) -> &Descr {
|
||||
&self.descr
|
||||
}
|
||||
|
||||
fn read_file_into(
|
||||
&self,
|
||||
path: &str,
|
||||
into: &mut dyn std::io::Write,
|
||||
) -> Result<(), 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)?
|
||||
.peel_to_tree()?
|
||||
.lookup_entry_by_path(clean_path)?
|
||||
.ok_or(ReadFileIntoError::FileNotFound)?
|
||||
.object()?;
|
||||
|
||||
into.write_all(file_object.data.as_ref())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// git::Store implements the Store trait for any Descr::Git based Origins. If any non-git
|
||||
/// Descrs are used then this implementation will panic.
|
||||
pub struct Store<'a> {
|
||||
dir_path: &'a Path,
|
||||
}
|
||||
@ -115,12 +169,12 @@ pub mod git {
|
||||
.map_err(|_| SyncError::InvalidURL)?;
|
||||
|
||||
// Check to make sure the branch name exists
|
||||
// TODO if this fails we should delete the whole repo
|
||||
// TODO if this fails we should delete repo_path
|
||||
repo.try_find_reference(&Self::branch_ref(branch_name))?
|
||||
.ok_or(SyncError::InvalidBranchName)?;
|
||||
|
||||
// Add the descr to the repo directory, so we can know the actual descr later
|
||||
// TODO if this fails we should delete the whole repo
|
||||
// TODO if this fails we should delete repo_path
|
||||
let descr_file = fs::File::create(self.descr_file_path(descr.id().as_ref()))?;
|
||||
|
||||
serde_json::to_writer(descr_file, &descr)?;
|
||||
@ -144,41 +198,34 @@ pub mod git {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_file_into<W>(
|
||||
&self,
|
||||
descr: &Descr,
|
||||
path: &str,
|
||||
into: &mut W,
|
||||
) -> Result<(), WriteFileError>
|
||||
where
|
||||
W: io::Write + ?Sized,
|
||||
{
|
||||
let Descr::Git { branch_name, .. } = descr;
|
||||
let repo_path = &self.repo_path(descr);
|
||||
let branch_ref = &Self::branch_ref(branch_name);
|
||||
fn get(&self, descr: Descr) -> Result<Box<dyn super::Origin>, GetError> {
|
||||
let repo_path = self.repo_path(&descr);
|
||||
let Descr::Git {
|
||||
ref branch_name, ..
|
||||
} = descr;
|
||||
|
||||
let mut clean_path = Path::new(path);
|
||||
clean_path = clean_path.strip_prefix("/").unwrap_or(clean_path);
|
||||
fs::read_dir(&repo_path).map_err(|e| match e.kind() {
|
||||
io::ErrorKind::NotFound => GetError::NotFound,
|
||||
_ => e.into(),
|
||||
})?;
|
||||
|
||||
let repo = gix::open(repo_path)?;
|
||||
let repo = gix::open(&repo_path)?;
|
||||
|
||||
let commit_object_id = repo.find_reference(branch_ref)?.peel_to_id_in_place()?;
|
||||
let commit_object_id = repo
|
||||
.find_reference(&Self::branch_ref(branch_name))?
|
||||
.peel_to_id_in_place()?
|
||||
.detach();
|
||||
|
||||
let tree_object_id = repo
|
||||
.find_object(commit_object_id)?
|
||||
.try_to_commit_ref()?
|
||||
.tree();
|
||||
|
||||
let file_object = repo
|
||||
.find_object(tree_object_id)?
|
||||
.peel_to_tree()?
|
||||
.lookup_entry_by_path(clean_path)?
|
||||
.ok_or(WriteFileError::FileNotFound)?
|
||||
.object()?;
|
||||
|
||||
into.write_all(file_object.data.as_ref())?;
|
||||
|
||||
Ok(())
|
||||
return Ok(Box::from(Origin {
|
||||
descr,
|
||||
repo,
|
||||
tree_object_id,
|
||||
}));
|
||||
}
|
||||
|
||||
fn all_descrs(
|
||||
@ -212,7 +259,7 @@ pub mod git {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::Store;
|
||||
use super::super::{Origin, Store};
|
||||
use super::Descr;
|
||||
use tempdir::TempDir;
|
||||
|
||||
@ -223,10 +270,15 @@ pub mod git {
|
||||
let curr_dir = format!("file://{}", std::env::current_dir().unwrap().display());
|
||||
|
||||
let descr = Descr::Git {
|
||||
url: curr_dir,
|
||||
url: curr_dir.clone(),
|
||||
branch_name: String::from("master"),
|
||||
};
|
||||
|
||||
let other_descr = Descr::Git {
|
||||
url: curr_dir.clone(),
|
||||
branch_name: String::from("some_other_branch"),
|
||||
};
|
||||
|
||||
let limits = super::Limits {};
|
||||
|
||||
let store = super::Store::new(tmp_dir.path()).expect("store created");
|
||||
@ -236,10 +288,19 @@ pub mod git {
|
||||
.sync(&descr, limits)
|
||||
.expect("second sync should succeed");
|
||||
|
||||
assert!(matches!(
|
||||
store.get(other_descr),
|
||||
Err::<Box<dyn Origin>, super::GetError>(super::GetError::NotFound),
|
||||
));
|
||||
|
||||
let origin = store.get(descr.clone()).expect("origin retrieved");
|
||||
|
||||
assert_eq!(&descr, origin.descr());
|
||||
|
||||
let assert_write = |path: &str| {
|
||||
let mut into: Vec<u8> = vec![];
|
||||
store
|
||||
.read_file_into(&descr, path, &mut into)
|
||||
origin
|
||||
.read_file_into(path, &mut into)
|
||||
.expect("write should succeed");
|
||||
assert!(into.len() > 0);
|
||||
};
|
||||
@ -250,8 +311,8 @@ pub mod git {
|
||||
// File doesn't exist
|
||||
let mut into: Vec<u8> = vec![];
|
||||
assert!(matches!(
|
||||
store.read_file_into(&descr, "DNE", &mut into),
|
||||
Err::<(), super::WriteFileError>(super::WriteFileError::FileNotFound),
|
||||
origin.read_file_into("DNE", &mut into),
|
||||
Err::<(), super::ReadFileIntoError>(super::ReadFileIntoError::FileNotFound),
|
||||
));
|
||||
assert_eq!(into.len(), 0);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user