implement git store write_file
This commit is contained in:
parent
2663838af0
commit
a4febdc40e
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user