|
|
@ -8,10 +8,17 @@ use super::Descr; |
|
|
|
pub struct Limits {} |
|
|
|
pub struct Limits {} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
#[derive(Debug)] |
|
|
|
pub enum AllDescrsError { |
|
|
|
pub enum WriteFileError { |
|
|
|
|
|
|
|
FileNotFound, |
|
|
|
Unexpected(Box<dyn Error>), |
|
|
|
Unexpected(Box<dyn Error>), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<E: Error + 'static> From<E> for WriteFileError { |
|
|
|
|
|
|
|
fn from(e: E) -> WriteFileError { |
|
|
|
|
|
|
|
WriteFileError::Unexpected(Box::from(e)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
#[derive(Debug)] |
|
|
|
pub enum SyncError { |
|
|
|
pub enum SyncError { |
|
|
|
InvalidURL, |
|
|
|
InvalidURL, |
|
|
@ -19,12 +26,25 @@ pub enum SyncError { |
|
|
|
Unexpected(Box<dyn Error>), |
|
|
|
Unexpected(Box<dyn Error>), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<E: Error + 'static> From<E> for SyncError { |
|
|
|
|
|
|
|
fn from(e: E) -> SyncError { |
|
|
|
|
|
|
|
SyncError::Unexpected(Box::from(e)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
#[derive(Debug)] |
|
|
|
pub enum WriteFileError { |
|
|
|
pub enum AllDescrsError { |
|
|
|
FileNotFound, |
|
|
|
|
|
|
|
Unexpected(Box<dyn Error>), |
|
|
|
Unexpected(Box<dyn Error>), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<E: Error + 'static> From<E> for AllDescrsError { |
|
|
|
|
|
|
|
fn from(e: E) -> AllDescrsError { |
|
|
|
|
|
|
|
AllDescrsError::Unexpected(Box::from(e)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type AllDescrsResult<T> = Result<T, AllDescrsError>; |
|
|
|
|
|
|
|
|
|
|
|
pub trait Store { |
|
|
|
pub trait Store { |
|
|
|
fn write_file<W>(&self, descr: &Descr, path: &str, into: &mut W) -> Result<(), WriteFileError> |
|
|
|
fn write_file<W>(&self, descr: &Descr, path: &str, into: &mut W) -> Result<(), WriteFileError> |
|
|
|
where |
|
|
|
where |
|
|
@ -32,9 +52,7 @@ pub trait Store { |
|
|
|
|
|
|
|
|
|
|
|
fn sync(&self, descr: &Descr, limits: Limits) -> Result<(), SyncError>; |
|
|
|
fn sync(&self, descr: &Descr, limits: Limits) -> Result<(), SyncError>; |
|
|
|
|
|
|
|
|
|
|
|
fn all_descrs( |
|
|
|
fn all_descrs(&self) -> AllDescrsResult<Box<dyn Iterator<Item = AllDescrsResult<Descr>> + '_>>; |
|
|
|
&self, |
|
|
|
|
|
|
|
) -> Result<Box<dyn Iterator<Item = Result<Descr, AllDescrsError>> + '_>, AllDescrsError>; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub mod git { |
|
|
|
pub mod git { |
|
|
@ -43,24 +61,8 @@ pub mod git { |
|
|
|
use std::path::{Path, PathBuf}; |
|
|
|
use std::path::{Path, PathBuf}; |
|
|
|
use std::{fs, io}; |
|
|
|
use std::{fs, io}; |
|
|
|
|
|
|
|
|
|
|
|
use gix::clone::Error as gixCloneErr; |
|
|
|
|
|
|
|
use gix::progress::Discard; |
|
|
|
use gix::progress::Discard; |
|
|
|
|
|
|
|
|
|
|
|
// Catching error from gix::prepare_clone_bare
|
|
|
|
|
|
|
|
impl From<gixCloneErr> for SyncError { |
|
|
|
|
|
|
|
fn from(e: gixCloneErr) -> SyncError { |
|
|
|
|
|
|
|
match e { |
|
|
|
|
|
|
|
gixCloneErr::Init(gix::init::Error::InvalidBranchName { .. }) => { |
|
|
|
|
|
|
|
SyncError::InvalidBranchName |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
gixCloneErr::UrlParse(_) | gixCloneErr::CanonicalizeUrl { .. } => { |
|
|
|
|
|
|
|
SyncError::InvalidURL |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
_ => SyncError::Unexpected(Box::from(e)), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub struct Store<'a> { |
|
|
|
pub struct Store<'a> { |
|
|
|
dir_path: &'a Path, |
|
|
|
dir_path: &'a Path, |
|
|
|
} |
|
|
|
} |
|
|
@ -93,47 +95,51 @@ pub mod git { |
|
|
|
// 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 fs::read_dir(repo_path).is_err() { |
|
|
|
if fs::read_dir(repo_path).is_err() { |
|
|
|
fs::create_dir_all(repo_path).map_err(|e| SyncError::Unexpected(Box::from(e)))?; |
|
|
|
fs::create_dir_all(repo_path)?; |
|
|
|
|
|
|
|
|
|
|
|
let Descr::Git { url, branch_name } = descr; |
|
|
|
let Descr::Git { url, branch_name } = descr; |
|
|
|
|
|
|
|
|
|
|
|
let (repo, _) = gix::prepare_clone_bare(url.clone(), repo_path)? |
|
|
|
use gix::clone::Error as gixCloneErr; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let (repo, _) = gix::prepare_clone_bare(url.clone(), repo_path) |
|
|
|
|
|
|
|
.map_err(|e| match e { |
|
|
|
|
|
|
|
gixCloneErr::Init(gix::init::Error::InvalidBranchName { .. }) => { |
|
|
|
|
|
|
|
SyncError::InvalidBranchName |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
gixCloneErr::UrlParse(_) | gixCloneErr::CanonicalizeUrl { .. } => { |
|
|
|
|
|
|
|
SyncError::InvalidURL |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
_ => SyncError::Unexpected(Box::from(e)), |
|
|
|
|
|
|
|
})? |
|
|
|
.fetch_only(Discard, should_interrupt) |
|
|
|
.fetch_only(Discard, should_interrupt) |
|
|
|
.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 the whole repo
|
|
|
|
repo.try_find_reference(&Self::branch_ref(branch_name)) |
|
|
|
repo.try_find_reference(&Self::branch_ref(branch_name))? |
|
|
|
.map_err(|e| SyncError::Unexpected(Box::from(e)))? |
|
|
|
|
|
|
|
.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 the whole repo
|
|
|
|
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()))?; |
|
|
|
.map_err(|e| SyncError::Unexpected(Box::from(e)))?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
serde_json::to_writer(descr_file, &descr) |
|
|
|
serde_json::to_writer(descr_file, &descr)?; |
|
|
|
.map_err(|e| SyncError::Unexpected(Box::from(e)))?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Ok(()); |
|
|
|
return Ok(()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let direction = gix::remote::Direction::Fetch; |
|
|
|
let direction = gix::remote::Direction::Fetch; |
|
|
|
|
|
|
|
|
|
|
|
let repo = gix::open(repo_path).map_err(|e| SyncError::Unexpected(Box::from(e)))?; |
|
|
|
let repo = gix::open(repo_path)?; |
|
|
|
|
|
|
|
|
|
|
|
let remote = repo |
|
|
|
let remote = repo |
|
|
|
.find_default_remote(direction) |
|
|
|
.find_default_remote(direction) |
|
|
|
.ok_or_else(|| SyncError::Unexpected(Box::from("no default configured")))? |
|
|
|
.ok_or_else(|| SyncError::Unexpected(Box::from("no default configured")))??; |
|
|
|
.map_err(|e| SyncError::Unexpected(Box::from(e)))?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
remote |
|
|
|
remote |
|
|
|
.connect(direction) |
|
|
|
.connect(direction)? |
|
|
|
.map_err(|e| SyncError::Unexpected(Box::from(e)))? |
|
|
|
.prepare_fetch(Discard, Default::default())? |
|
|
|
.prepare_fetch(Discard, Default::default()) |
|
|
|
.receive(Discard, should_interrupt)?; |
|
|
|
.map_err(|e| SyncError::Unexpected(Box::from(e)))? |
|
|
|
|
|
|
|
.receive(Discard, should_interrupt) |
|
|
|
|
|
|
|
.map_err(|e| SyncError::Unexpected(Box::from(e)))?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
@ -154,69 +160,51 @@ pub mod git { |
|
|
|
let mut clean_path = Path::new(path); |
|
|
|
let mut clean_path = Path::new(path); |
|
|
|
clean_path = clean_path.strip_prefix("/").unwrap_or(clean_path); |
|
|
|
clean_path = clean_path.strip_prefix("/").unwrap_or(clean_path); |
|
|
|
|
|
|
|
|
|
|
|
let repo = |
|
|
|
let repo = gix::open(repo_path)?; |
|
|
|
gix::open(repo_path).map_err(|e| WriteFileError::Unexpected(Box::from(e)))?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let commit_object_id = repo |
|
|
|
let commit_object_id = repo.find_reference(branch_ref)?.peel_to_id_in_place()?; |
|
|
|
.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 |
|
|
|
let tree_object_id = repo |
|
|
|
.find_object(commit_object_id) |
|
|
|
.find_object(commit_object_id)? |
|
|
|
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))? |
|
|
|
.try_to_commit_ref()? |
|
|
|
.try_to_commit_ref() |
|
|
|
|
|
|
|
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))? |
|
|
|
|
|
|
|
.tree(); |
|
|
|
.tree(); |
|
|
|
|
|
|
|
|
|
|
|
let file_object = repo |
|
|
|
let file_object = repo |
|
|
|
.find_object(tree_object_id) |
|
|
|
.find_object(tree_object_id)? |
|
|
|
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))? |
|
|
|
.peel_to_tree()? |
|
|
|
.peel_to_tree() |
|
|
|
.lookup_entry_by_path(clean_path)? |
|
|
|
.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)? |
|
|
|
.ok_or(WriteFileError::FileNotFound)? |
|
|
|
.object() |
|
|
|
.object()?; |
|
|
|
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
into.write_all(file_object.data.as_ref()) |
|
|
|
into.write_all(file_object.data.as_ref())?; |
|
|
|
.map_err(|e| WriteFileError::Unexpected(Box::from(e)))?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn all_descrs( |
|
|
|
fn all_descrs( |
|
|
|
&self, |
|
|
|
&self, |
|
|
|
) -> Result<Box<dyn Iterator<Item = Result<Descr, AllDescrsError>> + '_>, AllDescrsError> |
|
|
|
) -> AllDescrsResult<Box<dyn Iterator<Item = AllDescrsResult<Descr>> + '_>> { |
|
|
|
{ |
|
|
|
let inner_iter = fs::read_dir(self.dir_path)?.map( |
|
|
|
let inner_iter = fs::read_dir(self.dir_path) |
|
|
|
|dir_entry_res: io::Result<fs::DirEntry>| -> Result<Descr, AllDescrsError> { |
|
|
|
.map_err(|e| AllDescrsError::Unexpected(Box::from(e)))? |
|
|
|
let descr_id: String = dir_entry_res? |
|
|
|
.map( |
|
|
|
.file_name() |
|
|
|
|dir_entry_res: io::Result<fs::DirEntry>| -> Result<Descr, AllDescrsError> { |
|
|
|
.to_str() |
|
|
|
let descr_id: String = dir_entry_res |
|
|
|
.ok_or_else(|| { |
|
|
|
.map_err(|e| AllDescrsError::Unexpected(Box::from(e)))? |
|
|
|
AllDescrsError::Unexpected(Box::from( |
|
|
|
.file_name() |
|
|
|
"couldn't convert os string to &str", |
|
|
|
.to_str() |
|
|
|
)) |
|
|
|
.ok_or_else(|| { |
|
|
|
})? |
|
|
|
AllDescrsError::Unexpected(Box::from( |
|
|
|
.into(); |
|
|
|
"couldn't convert os string to &str", |
|
|
|
|
|
|
|
)) |
|
|
|
let descr_file_path = self.descr_file_path(descr_id.as_ref()); |
|
|
|
})? |
|
|
|
|
|
|
|
.into(); |
|
|
|
let descr_file = fs::File::open(descr_file_path)?; |
|
|
|
|
|
|
|
|
|
|
|
let descr_file_path = self.descr_file_path(descr_id.as_ref()); |
|
|
|
let descr = serde_json::from_reader(descr_file)?; |
|
|
|
|
|
|
|
|
|
|
|
let descr_file = fs::File::open(descr_file_path) |
|
|
|
Ok(descr) |
|
|
|
.map_err(|e| AllDescrsError::Unexpected(Box::from(e)))?; |
|
|
|
}, |
|
|
|
|
|
|
|
); |
|
|
|
let descr = serde_json::from_reader(descr_file) |
|
|
|
|
|
|
|
.map_err(|e| AllDescrsError::Unexpected(Box::from(e)))?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(descr) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(Box::from(inner_iter)) |
|
|
|
Ok(Box::from(inner_iter)) |
|
|
|
} |
|
|
|
} |
|
|
|