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;
|
use std::error::Error;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Limits {}
|
pub struct Limits {
|
||||||
|
// TODO storage limits
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum WriteFileError {
|
pub enum ReadFileIntoError {
|
||||||
FileNotFound,
|
FileNotFound,
|
||||||
Unexpected(Box<dyn Error>),
|
Unexpected(Box<dyn Error>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Error + 'static> From<E> for WriteFileError {
|
impl<E: Error + 'static> From<E> for ReadFileIntoError {
|
||||||
fn from(e: E) -> WriteFileError {
|
fn from(e: E) -> ReadFileIntoError {
|
||||||
WriteFileError::Unexpected(Box::from(e))
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum AllDescrsError {
|
pub enum AllDescrsError {
|
||||||
Unexpected(Box<dyn Error>),
|
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>;
|
type AllDescrsResult<T> = Result<T, AllDescrsError>;
|
||||||
|
|
||||||
pub trait Store {
|
pub trait Origin {
|
||||||
fn read_file_into<W>(
|
fn descr(&self) -> &Descr;
|
||||||
|
fn read_file_into(
|
||||||
&self,
|
&self,
|
||||||
descr: &Descr,
|
|
||||||
path: &str,
|
path: &str,
|
||||||
into: &mut W,
|
into: &mut dyn std::io::Write,
|
||||||
) -> Result<(), WriteFileError>
|
) -> Result<(), ReadFileIntoError>;
|
||||||
where
|
}
|
||||||
W: std::io::Write + ?Sized;
|
|
||||||
|
|
||||||
|
/// 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 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>> + '_>>;
|
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::path::{Path, PathBuf};
|
||||||
use std::{fs, io};
|
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> {
|
pub struct Store<'a> {
|
||||||
dir_path: &'a Path,
|
dir_path: &'a Path,
|
||||||
}
|
}
|
||||||
@ -115,12 +169,12 @@ pub mod git {
|
|||||||
.map_err(|_| SyncError::InvalidURL)?;
|
.map_err(|_| SyncError::InvalidURL)?;
|
||||||
|
|
||||||
// Check to make sure the branch name exists
|
// 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))?
|
repo.try_find_reference(&Self::branch_ref(branch_name))?
|
||||||
.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 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()))?;
|
let descr_file = fs::File::create(self.descr_file_path(descr.id().as_ref()))?;
|
||||||
|
|
||||||
serde_json::to_writer(descr_file, &descr)?;
|
serde_json::to_writer(descr_file, &descr)?;
|
||||||
@ -144,41 +198,34 @@ pub mod git {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_file_into<W>(
|
fn get(&self, descr: Descr) -> Result<Box<dyn super::Origin>, GetError> {
|
||||||
&self,
|
let repo_path = self.repo_path(&descr);
|
||||||
descr: &Descr,
|
let Descr::Git {
|
||||||
path: &str,
|
ref branch_name, ..
|
||||||
into: &mut W,
|
} = descr;
|
||||||
) -> 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);
|
|
||||||
|
|
||||||
let mut clean_path = Path::new(path);
|
fs::read_dir(&repo_path).map_err(|e| match e.kind() {
|
||||||
clean_path = clean_path.strip_prefix("/").unwrap_or(clean_path);
|
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
|
let tree_object_id = repo
|
||||||
.find_object(commit_object_id)?
|
.find_object(commit_object_id)?
|
||||||
.try_to_commit_ref()?
|
.try_to_commit_ref()?
|
||||||
.tree();
|
.tree();
|
||||||
|
|
||||||
let file_object = repo
|
return Ok(Box::from(Origin {
|
||||||
.find_object(tree_object_id)?
|
descr,
|
||||||
.peel_to_tree()?
|
repo,
|
||||||
.lookup_entry_by_path(clean_path)?
|
tree_object_id,
|
||||||
.ok_or(WriteFileError::FileNotFound)?
|
}));
|
||||||
.object()?;
|
|
||||||
|
|
||||||
into.write_all(file_object.data.as_ref())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn all_descrs(
|
fn all_descrs(
|
||||||
@ -212,7 +259,7 @@ pub mod git {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::super::Store;
|
use super::super::{Origin, Store};
|
||||||
use super::Descr;
|
use super::Descr;
|
||||||
use tempdir::TempDir;
|
use tempdir::TempDir;
|
||||||
|
|
||||||
@ -223,10 +270,15 @@ pub mod git {
|
|||||||
let curr_dir = format!("file://{}", std::env::current_dir().unwrap().display());
|
let curr_dir = format!("file://{}", std::env::current_dir().unwrap().display());
|
||||||
|
|
||||||
let descr = Descr::Git {
|
let descr = Descr::Git {
|
||||||
url: curr_dir,
|
url: curr_dir.clone(),
|
||||||
branch_name: String::from("master"),
|
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 limits = super::Limits {};
|
||||||
|
|
||||||
let store = super::Store::new(tmp_dir.path()).expect("store created");
|
let store = super::Store::new(tmp_dir.path()).expect("store created");
|
||||||
@ -236,10 +288,19 @@ pub mod git {
|
|||||||
.sync(&descr, limits)
|
.sync(&descr, limits)
|
||||||
.expect("second sync should succeed");
|
.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 assert_write = |path: &str| {
|
||||||
let mut into: Vec<u8> = vec![];
|
let mut into: Vec<u8> = vec![];
|
||||||
store
|
origin
|
||||||
.read_file_into(&descr, path, &mut into)
|
.read_file_into(path, &mut into)
|
||||||
.expect("write should succeed");
|
.expect("write should succeed");
|
||||||
assert!(into.len() > 0);
|
assert!(into.len() > 0);
|
||||||
};
|
};
|
||||||
@ -250,8 +311,8 @@ pub mod git {
|
|||||||
// File doesn't exist
|
// File doesn't exist
|
||||||
let mut into: Vec<u8> = vec![];
|
let mut into: Vec<u8> = vec![];
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
store.read_file_into(&descr, "DNE", &mut into),
|
origin.read_file_into("DNE", &mut into),
|
||||||
Err::<(), super::WriteFileError>(super::WriteFileError::FileNotFound),
|
Err::<(), super::ReadFileIntoError>(super::ReadFileIntoError::FileNotFound),
|
||||||
));
|
));
|
||||||
assert_eq!(into.len(), 0);
|
assert_eq!(into.len(), 0);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user