refactor Store to return an Origin

This commit is contained in:
Brian Picciano 2023-05-08 13:47:21 +02:00
parent 563f072e09
commit 53e253b362

View File

@ -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);