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 use self::descr::Descr;
|
||||||
|
|
||||||
pub mod store;
|
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 std::error::Error;
|
||||||
|
|
||||||
use super::Descr;
|
use super::Descr;
|
||||||
use super::View;
|
|
||||||
|
|
||||||
#[derive(Clone,Copy)]
|
#[derive(Clone,Copy)]
|
||||||
pub struct Limits {}
|
pub struct Limits {}
|
||||||
|
|
||||||
pub enum GetViewError {
|
|
||||||
TODO,
|
|
||||||
Unexpected(Box<dyn Error>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SyncError {
|
pub enum SyncError {
|
||||||
InvalidURL,
|
InvalidURL,
|
||||||
@ -18,15 +12,29 @@ pub enum SyncError {
|
|||||||
Unexpected(Box<dyn Error>),
|
Unexpected(Box<dyn Error>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum WriteFileError {
|
||||||
|
FileNotFound,
|
||||||
|
Unexpected(Box<dyn Error>),
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Store {
|
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>;
|
fn sync(&self, descr: Descr, limits: Limits) -> Result<(), SyncError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod git {
|
pub mod git {
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use super::{Store as originStore, *};
|
use super::*;
|
||||||
|
|
||||||
use gix::clone::Error as gixCloneErr;
|
use gix::clone::Error as gixCloneErr;
|
||||||
use gix::progress::Discard;
|
use gix::progress::Discard;
|
||||||
@ -54,38 +62,49 @@ pub mod git {
|
|||||||
|
|
||||||
pub fn new(dir_path: &Path) -> std::io::Result<Store> {
|
pub fn new(dir_path: &Path) -> std::io::Result<Store> {
|
||||||
std::fs::create_dir_all(dir_path)?;
|
std::fs::create_dir_all(dir_path)?;
|
||||||
println!("{}", &dir_path.display());
|
|
||||||
Ok(Store{dir_path})
|
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> {
|
fn sync(&self, descr: Descr, _limits: Limits) -> Result<(), SyncError> {
|
||||||
|
|
||||||
let should_interrupt = &core::sync::atomic::AtomicBool::new(false);
|
let should_interrupt = &core::sync::atomic::AtomicBool::new(false);
|
||||||
|
|
||||||
let Descr::Git{url, ..} = descr;
|
let repo_path = &self.repo_path(descr);
|
||||||
|
|
||||||
let path = &self.dir_path.clone().join(descr.id());
|
|
||||||
|
|
||||||
// if the path doesn't exist then use the gix clone feature to clone it into the
|
// if the path doesn't exist then use the gix clone feature to clone it into the
|
||||||
// directory.
|
// 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)))?;
|
.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)
|
.fetch_only(Discard, should_interrupt)
|
||||||
.map_err(|_| SyncError::InvalidURL)?;
|
.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(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let direction = gix::remote::Direction::Fetch;
|
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)))?;
|
.map_err(|e| SyncError::Unexpected(Box::from(e)))?;
|
||||||
|
|
||||||
let remote = repo.find_default_remote(direction)
|
let remote = repo.find_default_remote(direction)
|
||||||
@ -103,10 +122,52 @@ pub mod git {
|
|||||||
Ok(())
|
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)]
|
#[cfg(test)]
|
||||||
@ -118,12 +179,14 @@ pub mod git {
|
|||||||
use tempdir::TempDir;
|
use tempdir::TempDir;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sync() {
|
fn basic() {
|
||||||
|
|
||||||
let tmp_dir = TempDir::new("origin_store_git").unwrap();
|
let tmp_dir = TempDir::new("origin_store_git").unwrap();
|
||||||
|
|
||||||
|
let curr_dir = format!("file://{}", std::env::current_dir().unwrap().display());
|
||||||
|
|
||||||
let descr = Descr::Git{
|
let descr = Descr::Git{
|
||||||
url: "https://github.com/mediocregopher/priority-finder.git",
|
url: curr_dir.as_ref(),
|
||||||
branch_name: "master",
|
branch_name: "master",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -133,6 +196,23 @@ pub mod git {
|
|||||||
|
|
||||||
store.sync(descr, limits).expect("sync should succeed");
|
store.sync(descr, limits).expect("sync should succeed");
|
||||||
store.sync(descr, limits).expect("second 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