implement git store write_file

This commit is contained in:
Brian Picciano 2023-05-04 18:13:44 +02:00
parent 2663838af0
commit a4febdc40e
2 changed files with 103 additions and 29 deletions

View File

@ -2,9 +2,3 @@ mod descr;
pub use self::descr::Descr;
pub mod store;
/// A View of an origin is used to read the files of that origin at a particular point in time. A
/// single instance of a View should remain consistent across its lifetime.
pub trait View {
fn open(&self, path: &str) -> &dyn std::io::Read;
}

View File

@ -1,16 +1,10 @@
use std::error::Error;
use super::Descr;
use super::View;
#[derive(Clone,Copy)]
pub struct Limits {}
pub enum GetViewError {
TODO,
Unexpected(Box<dyn Error>),
}
#[derive(Debug)]
pub enum SyncError {
InvalidURL,
@ -18,15 +12,29 @@ pub enum SyncError {
Unexpected(Box<dyn Error>),
}
#[derive(Debug)]
pub enum WriteFileError {
FileNotFound,
Unexpected(Box<dyn Error>),
}
pub trait Store {
fn get_view(&self, descr: Descr) -> Result<&dyn View, GetViewError>;
fn write_file<W>(
&self,
descr: Descr,
path: &str,
into: &mut W,
) -> Result<(), WriteFileError>
where W: std::io::Write + ?Sized;
fn sync(&self, descr: Descr, limits: Limits) -> Result<(), SyncError>;
}
pub mod git {
use std::path::Path;
use super::{Store as originStore, *};
use std::path::{Path, PathBuf};
use super::*;
use gix::clone::Error as gixCloneErr;
use gix::progress::Discard;
@ -54,38 +62,49 @@ pub mod git {
pub fn new(dir_path: &Path) -> std::io::Result<Store> {
std::fs::create_dir_all(dir_path)?;
println!("{}", &dir_path.display());
Ok(Store{dir_path})
}
fn repo_path(&self, descr: Descr) -> PathBuf {
self.dir_path.clone().join(descr.id())
}
fn branch_ref(branch_name: &str) -> String {
format!("origin/{branch_name}")
}
}
impl<'a> originStore for Store<'a> {
impl<'a> super::Store for Store<'a> {
fn sync(&self, descr: Descr, _limits: Limits) -> Result<(), SyncError> {
let should_interrupt = &core::sync::atomic::AtomicBool::new(false);
let Descr::Git{url, ..} = descr;
let path = &self.dir_path.clone().join(descr.id());
let repo_path = &self.repo_path(descr);
// if the path doesn't exist then use the gix clone feature to clone it into the
// directory.
if std::fs::read_dir(path).is_err() {
if std::fs::read_dir(repo_path).is_err() {
std::fs::create_dir_all(path)
std::fs::create_dir_all(repo_path)
.map_err(|e| SyncError::Unexpected(Box::from(e)))?;
gix::prepare_clone_bare(url, path)?
let Descr::Git{url, branch_name} = descr;
let (repo, _) = gix::prepare_clone_bare(url, repo_path)?
.fetch_only(Discard, should_interrupt)
.map_err(|_| SyncError::InvalidURL)?;
repo.try_find_reference(&Self::branch_ref(branch_name))
.map_err(|e| SyncError::Unexpected(Box::from(e)))?
.ok_or(SyncError::InvalidBranchName)?;
return Ok(());
}
let direction = gix::remote::Direction::Fetch;
let repo = gix::open(path)
let repo = gix::open(repo_path)
.map_err(|e| SyncError::Unexpected(Box::from(e)))?;
let remote = repo.find_default_remote(direction)
@ -103,10 +122,52 @@ pub mod git {
Ok(())
}
fn get_view(&self, _descr: Descr) -> Result<&dyn View, GetViewError> {
Err(GetViewError::TODO)
}
fn write_file<W>(
&self,
descr: Descr,
path: &str,
into: &mut W,
) -> Result<(), WriteFileError>
where W: std::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);
clean_path = clean_path.strip_prefix("/").unwrap_or(clean_path);
let repo = gix::open(repo_path)
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?;
let commit_object_id = repo
.find_reference(branch_ref)
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?
.peel_to_id_in_place()
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?;
let tree_object_id = repo.find_object(commit_object_id)
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?
.try_to_commit_ref()
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?
.tree();
let file_object = repo.find_object(tree_object_id)
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?
.peel_to_tree()
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?
.lookup_entry_by_path(clean_path)
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?
.ok_or(WriteFileError::FileNotFound)?
.object()
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?;
into.write_all(file_object.data.as_ref())
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?;
Ok(())
}
}
#[cfg(test)]
@ -118,12 +179,14 @@ pub mod git {
use tempdir::TempDir;
#[test]
fn sync() {
fn basic() {
let tmp_dir = TempDir::new("origin_store_git").unwrap();
let curr_dir = format!("file://{}", std::env::current_dir().unwrap().display());
let descr = Descr::Git{
url: "https://github.com/mediocregopher/priority-finder.git",
url: curr_dir.as_ref(),
branch_name: "master",
};
@ -133,6 +196,23 @@ pub mod git {
store.sync(descr, limits).expect("sync should succeed");
store.sync(descr, limits).expect("second sync should succeed");
let assert_write = |path: &str| {
let mut into: Vec<u8> = vec![];
store.write_file(descr, path, &mut into).expect("write should succeed");
assert!(into.len() > 0);
};
assert_write("src/lib.rs");
assert_write("/src/lib.rs");
// File doesn't exist
let mut into: Vec<u8> = vec![];
assert!(matches!(
store.write_file(descr, "DNE", &mut into),
Err::<(), super::WriteFileError>(super::WriteFileError::FileNotFound),
));
assert_eq!(into.len(), 0);
}
}
}