From 53e253b3623606b2c84dee13ab9fb2b112f589c1 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Mon, 8 May 2023 13:47:21 +0200 Subject: [PATCH] refactor Store to return an Origin --- src/origin/store.rs | 153 +++++++++++++++++++++++++++++++------------- 1 file changed, 107 insertions(+), 46 deletions(-) diff --git a/src/origin/store.rs b/src/origin/store.rs index a141daf..22189fa 100644 --- a/src/origin/store.rs +++ b/src/origin/store.rs @@ -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), } -impl From for WriteFileError { - fn from(e: E) -> WriteFileError { - WriteFileError::Unexpected(Box::from(e)) +impl From for ReadFileIntoError { + fn from(e: E) -> ReadFileIntoError { + ReadFileIntoError::Unexpected(Box::from(e)) } } @@ -29,6 +31,18 @@ impl From for SyncError { } } +#[derive(Debug)] +pub enum GetError { + NotFound, + Unexpected(Box), +} + +impl From for GetError { + fn from(e: E) -> GetError { + GetError::Unexpected(Box::from(e)) + } +} + #[derive(Debug)] pub enum AllDescrsError { Unexpected(Box), @@ -40,20 +54,25 @@ impl From for AllDescrsError { } } +/// Used in the return from all_descrs from Store. type AllDescrsResult = Result; -pub trait Store { - fn read_file_into( +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, GetError>; fn all_descrs(&self) -> AllDescrsResult> + '_>>; } @@ -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( - &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, 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::, 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 = 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 = 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);