diff --git a/src/domain/manager.rs b/src/domain/manager.rs index 834a742..13d5b71 100644 --- a/src/domain/manager.rs +++ b/src/domain/manager.rs @@ -25,19 +25,34 @@ impl From for GetConfigError { } #[derive(thiserror::Error, Debug)] -pub enum GetOriginError { - #[error("not found")] - NotFound, +pub enum ReadFileIntoError { + #[error("domain not found")] + DomainNotFound, + + #[error("file not found")] + FileNotFound, #[error(transparent)] Unexpected(#[from] unexpected::Error), } -impl From for GetOriginError { - fn from(e: config::GetError) -> GetOriginError { +impl From for ReadFileIntoError { + fn from(e: config::GetError) -> Self { match e { - config::GetError::NotFound => GetOriginError::NotFound, - config::GetError::Unexpected(e) => GetOriginError::Unexpected(e), + config::GetError::NotFound => Self::DomainNotFound, + config::GetError::Unexpected(e) => Self::Unexpected(e), + } + } +} + +impl From for ReadFileIntoError { + fn from(e: origin::ReadFileIntoError) -> Self { + match e { + origin::ReadFileIntoError::DescrNotSynced => { + Self::Unexpected(unexpected::Error::from("origin descr not synced")) + } + origin::ReadFileIntoError::FileNotFound => Self::FileNotFound, + origin::ReadFileIntoError::Unexpected(e) => Self::Unexpected(e), } } } @@ -84,13 +99,13 @@ pub enum SyncWithConfigError { Unexpected(#[from] unexpected::Error), } -impl From for SyncWithConfigError { - fn from(e: origin::store::SyncError) -> SyncWithConfigError { +impl From for SyncWithConfigError { + fn from(e: origin::SyncError) -> SyncWithConfigError { match e { - origin::store::SyncError::InvalidURL => SyncWithConfigError::InvalidURL, - origin::store::SyncError::InvalidBranchName => SyncWithConfigError::InvalidBranchName, - origin::store::SyncError::AlreadyInProgress => SyncWithConfigError::AlreadyInProgress, - origin::store::SyncError::Unexpected(e) => SyncWithConfigError::Unexpected(e), + origin::SyncError::InvalidURL => SyncWithConfigError::InvalidURL, + origin::SyncError::InvalidBranchName => SyncWithConfigError::InvalidBranchName, + origin::SyncError::AlreadyInProgress => SyncWithConfigError::AlreadyInProgress, + origin::SyncError::Unexpected(e) => SyncWithConfigError::Unexpected(e), } } } @@ -121,10 +136,12 @@ pub type GetAcmeHttp01ChallengeKeyError = acme::manager::GetHttp01ChallengeKeyEr pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert { fn get_config(&self, domain: &domain::Name) -> Result; - fn get_origin( + fn read_file_into( &self, domain: &domain::Name, - ) -> Result, GetOriginError>; + path: &str, + into: &mut dyn std::io::Write, + ) -> Result<(), ReadFileIntoError>; fn sync_cert<'mgr>( &'mgr self, @@ -146,7 +163,7 @@ pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert { } pub struct ManagerImpl { - origin_store: Box, + origin_store: Box, domain_config_store: Box, domain_checker: checker::DNSChecker, acme_manager: Option>, @@ -154,7 +171,7 @@ pub struct ManagerImpl { impl ManagerImpl { pub fn new< - OriginStore: origin::store::Store + Send + Sync + 'static, + OriginStore: origin::Store + Send + Sync + 'static, DomainConfigStore: config::Store + Send + Sync + 'static, AcmeManager: acme::manager::Manager + Send + Sync + 'static, >( @@ -212,17 +229,16 @@ impl Manager for ManagerImpl { Ok(self.domain_config_store.get(domain)?) } - fn get_origin( + fn read_file_into( &self, domain: &domain::Name, - ) -> Result, GetOriginError> { + path: &str, + into: &mut dyn std::io::Write, + ) -> Result<(), ReadFileIntoError> { let config = self.domain_config_store.get(domain)?; - let origin = self - .origin_store - .get(&config.origin_descr) - // if there's a config there should be an origin, any error here is unexpected - .or_unexpected()?; - Ok(origin) + self.origin_store + .read_file_into(&config.origin_descr, path, into)?; + Ok(()) } fn sync_cert<'mgr>( diff --git a/src/main.rs b/src/main.rs index e3d1c5c..d27bf5a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,7 +73,7 @@ async fn main() { ) .init(); - let origin_store = domani::origin::store::git::FSStore::new(config.origin_store_git_dir_path) + let origin_store = domani::origin::git::FSStore::new(config.origin_store_git_dir_path) .expect("git origin store initialization failed"); let domain_checker = domani::domain::checker::DNSChecker::new( diff --git a/src/origin.rs b/src/origin.rs index 2dfdda1..3522371 100644 --- a/src/origin.rs +++ b/src/origin.rs @@ -1,11 +1,38 @@ use crate::error::unexpected; +use std::sync; + +pub mod git; +pub mod mux; mod descr; -pub use self::descr::Descr; -pub mod store; +pub use descr::Descr; + +#[derive(thiserror::Error, Clone, Debug, PartialEq)] +pub enum SyncError { + #[error("invalid url")] + InvalidURL, + + #[error("invalid branch name")] + InvalidBranchName, + + #[error("already in progress")] + AlreadyInProgress, + + #[error(transparent)] + Unexpected(#[from] unexpected::Error), +} + +#[derive(thiserror::Error, Clone, Debug, PartialEq)] +pub enum AllDescrsError { + #[error(transparent)] + Unexpected(#[from] unexpected::Error), +} #[derive(thiserror::Error, Debug)] pub enum ReadFileIntoError { + #[error("descr not synced")] + DescrNotSynced, + #[error("file not found")] FileNotFound, @@ -14,12 +41,41 @@ pub enum ReadFileIntoError { } #[mockall::automock] -/// Describes an origin which has already been synced locally and is available for reading files -/// from. -pub trait Origin { +/// 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) -> Result<(), SyncError>; + fn read_file_into( &self, + descr: &Descr, path: &str, into: &mut dyn std::io::Write, ) -> Result<(), ReadFileIntoError>; + + fn all_descrs(&self) -> Result, AllDescrsError>; +} + +pub fn new_mock() -> sync::Arc> { + sync::Arc::new(sync::Mutex::new(MockStore::new())) +} + +impl Store for sync::Arc> { + fn sync(&self, descr: &Descr) -> Result<(), SyncError> { + self.lock().unwrap().sync(descr) + } + + fn all_descrs(&self) -> Result, AllDescrsError> { + self.lock().unwrap().all_descrs() + } + + fn read_file_into( + &self, + descr: &Descr, + path: &str, + into: &mut dyn std::io::Write, + ) -> Result<(), ReadFileIntoError> { + self.lock().unwrap().read_file_into(descr, path, into) + } } diff --git a/src/origin/store/git.rs b/src/origin/git.rs similarity index 70% rename from src/origin/store/git.rs rename to src/origin/git.rs index 89a2fe5..b1369a1 100644 --- a/src/origin/store/git.rs +++ b/src/origin/git.rs @@ -1,52 +1,17 @@ use crate::error::unexpected::{self, Intoable, Mappable}; -use crate::origin::{self, store}; +use crate::origin; use std::path::{Path, PathBuf}; use std::{collections, fs, io, sync}; #[derive(Clone)] -struct Origin { +struct RepoSnapshot { repo: sync::Arc, tree_object_id: gix::ObjectId, } -impl origin::Origin for Origin { - fn read_file_into( - &self, - path: &str, - into: &mut dyn std::io::Write, - ) -> Result<(), origin::ReadFileIntoError> { - let mut clean_path = Path::new(path); - clean_path = clean_path.strip_prefix("/").unwrap_or(clean_path); - - let repo = self.repo.to_thread_local(); - - let file_object = repo - .find_object(self.tree_object_id) - .map_unexpected_while(|| format!("finding tree object {}", self.tree_object_id))? - .peel_to_tree() - .map_unexpected_while(|| format!("peeling tree object {}", self.tree_object_id))? - .lookup_entry_by_path(clean_path) - .map_unexpected_while(|| { - format!( - "looking up {} in tree object {}", - clean_path.display(), - self.tree_object_id - ) - })? - .ok_or(origin::ReadFileIntoError::FileNotFound)? - .object() - .or_unexpected()?; - - into.write_all(file_object.data.as_ref()) - .or_unexpected_while("copying out file")?; - - Ok(()) - } -} - #[derive(thiserror::Error, Debug)] -enum GetOriginError { +enum CreateRepoSnapshotError { #[error("invalid branch name")] InvalidBranchName, @@ -54,8 +19,8 @@ enum GetOriginError { Unexpected(#[from] unexpected::Error), } -/// Implements the Store trait for any Descr::Git based Origins, storing the git repos on disk. If -/// any non-git Descrs are used then this implementation will panic. +/// Implements the Store trait for Descr::Git, storing the git repos on disk. If any non-git Descrs +/// are used then this implementation will panic. pub struct FSStore { dir_path: PathBuf, @@ -63,7 +28,7 @@ pub struct FSStore { // more than one origin to be syncing at a time sync_guard: sync::Mutex>, - origins: sync::RwLock>>, + repo_snapshots: sync::RwLock>>, } impl FSStore { @@ -72,7 +37,7 @@ impl FSStore { Ok(Self { dir_path, sync_guard: sync::Mutex::new(collections::HashMap::new()), - origins: sync::RwLock::new(collections::HashMap::new()), + repo_snapshots: sync::RwLock::new(collections::HashMap::new()), }) } @@ -88,11 +53,11 @@ impl FSStore { format!("origin/{branch_name}") } - fn get_origin( + fn create_repo_snapshot( &self, repo: gix::Repository, descr: &origin::Descr, - ) -> Result { + ) -> Result { let origin::Descr::Git { ref branch_name, .. } = descr; @@ -102,7 +67,7 @@ impl FSStore { let commit_object_id = repo .try_find_reference(&branch_ref) .map_unexpected_while(|| format!("finding branch ref {branch_ref}"))? - .ok_or(GetOriginError::InvalidBranchName)? + .ok_or(CreateRepoSnapshotError::InvalidBranchName)? .peel_to_id_in_place() .or_unexpected_while("peeling id in place")? .detach(); @@ -114,13 +79,60 @@ impl FSStore { .map_unexpected_while(|| format!("parsing {commit_object_id} as commit"))? .tree(); - Ok(Origin { + Ok(RepoSnapshot { repo: sync::Arc::new(repo.into()), tree_object_id, }) } - fn sync_inner(&self, descr: &origin::Descr) -> Result { + fn get_repo_snapshot( + &self, + descr: &origin::Descr, + ) -> Result>, unexpected::Error> { + { + let repo_snapshots = self.repo_snapshots.read().unwrap(); + if let Some(repo_snapshot) = repo_snapshots.get(descr) { + return Ok(Some(repo_snapshot.clone())); + } + } + + let repo_path = self.repo_path(descr); + + match fs::read_dir(&repo_path) { + Ok(_) => (), + Err(e) => match e.kind() { + io::ErrorKind::NotFound => return Ok(None), + _ => { + return Err(e.into_unexpected_while(format!( + "checking if {} exists", + repo_path.display() + ))) + } + }, + } + + let repo = gix::open(&repo_path) + .map_unexpected_while(|| format!("opening {} as git repo", repo_path.display()))?; + + let repo_snapshot = self + .create_repo_snapshot(repo, descr) + .map_err(|e| match e { + // it's not expected that the branch name is invalid at this point, it must have + // existed for sync to have been successful. + CreateRepoSnapshotError::InvalidBranchName => e.into_unexpected().into(), + CreateRepoSnapshotError::Unexpected(e) => e, + })?; + + let repo_snapshot = sync::Arc::new(repo_snapshot); + + let mut repo_snapshots = self.repo_snapshots.write().unwrap(); + + (*repo_snapshots).insert(descr.clone(), repo_snapshot.clone()); + + Ok(Some(repo_snapshot)) + } + + fn sync_inner(&self, descr: &origin::Descr) -> Result { use gix::clone::Error as gixCloneErr; use gix::progress::Discard; @@ -141,10 +153,10 @@ impl FSStore { let (repo, _) = gix::prepare_clone_bare(url.clone(), repo_path) .map_err(|e| match e { gixCloneErr::Init(gix::init::Error::InvalidBranchName { .. }) => { - store::SyncError::InvalidBranchName + origin::SyncError::InvalidBranchName } gixCloneErr::UrlParse(_) | gixCloneErr::CanonicalizeUrl { .. } => { - store::SyncError::InvalidURL + origin::SyncError::InvalidURL } _ => e .into_unexpected_while(format!( @@ -155,14 +167,14 @@ impl FSStore { .into(), })? .fetch_only(Discard, should_interrupt) - .map_err(|_| store::SyncError::InvalidURL)?; + .map_err(|_| origin::SyncError::InvalidURL)?; // Check to make sure the branch name exists // TODO if this fails we should delete repo_path let branch_ref = self.branch_ref(branch_name); repo.try_find_reference(&branch_ref) .map_unexpected_while(|| format!("finding branch ref {branch_ref}"))? - .ok_or(store::SyncError::InvalidBranchName)?; + .ok_or(origin::SyncError::InvalidBranchName)?; // Add the descr to the repo directory, so we can know the actual descr later // TODO if this fails we should delete repo_path @@ -199,7 +211,7 @@ impl FSStore { } impl super::Store for FSStore { - fn sync(&self, descr: &origin::Descr) -> Result<(), store::SyncError> { + fn sync(&self, descr: &origin::Descr) -> Result<(), origin::SyncError> { // attempt to lock this descr for syncing, doing so within a new scope so the mutex // isn't actually being held for the whole method duration. let is_already_syncing = { @@ -211,7 +223,7 @@ impl super::Store for FSStore { }; if is_already_syncing { - return Err(store::SyncError::AlreadyInProgress); + return Err(origin::SyncError::AlreadyInProgress); } let res = self.sync_inner(descr); @@ -224,7 +236,7 @@ impl super::Store for FSStore { }; // repo is synced at this point (though the sync lock is still held), just gotta create - // the origin and store it. + // the RepoSnapshot and store it. // // TODO this is a bit of a memory leak, but by the time we get // to that point this should all be backed by something which isn't local storage @@ -232,56 +244,22 @@ impl super::Store for FSStore { // calling this while the sync lock is held isn't ideal, but it's convenient and // shouldn't be too terrible generally - let origin = self.get_origin(repo, descr).map_err(|e| match e { - GetOriginError::InvalidBranchName => store::SyncError::InvalidBranchName, - GetOriginError::Unexpected(e) => store::SyncError::Unexpected(e), - })?; + let repo_snapshot = self + .create_repo_snapshot(repo, descr) + .map_err(|e| match e { + CreateRepoSnapshotError::InvalidBranchName => origin::SyncError::InvalidBranchName, + CreateRepoSnapshotError::Unexpected(e) => origin::SyncError::Unexpected(e), + })?; - let mut origins = self.origins.write().unwrap(); - (*origins).insert(descr.clone(), sync::Arc::new(origin)); + let mut repo_snapshots = self.repo_snapshots.write().unwrap(); + (*repo_snapshots).insert(descr.clone(), sync::Arc::new(repo_snapshot)); Ok(()) } - fn get(&self, descr: &origin::Descr) -> Result, store::GetError> { - { - let origins = self.origins.read().unwrap(); - if let Some(origin) = origins.get(descr) { - return Ok(origin.clone()); - } - } - - let repo_path = self.repo_path(descr); - - fs::read_dir(&repo_path).map_err(|e| match e.kind() { - io::ErrorKind::NotFound => store::GetError::NotFound, - _ => e - .into_unexpected_while(format!("checking if {} exists", repo_path.display())) - .into(), - })?; - - let repo = gix::open(&repo_path) - .map_unexpected_while(|| format!("opening {} as git repo", repo_path.display()))?; - - let origin = self.get_origin(repo, descr).map_err(|e| match e { - // it's not expected that the branch name is invalid at this point, it must have - // existed for sync to have been successful. - GetOriginError::InvalidBranchName => e.into_unexpected().into(), - GetOriginError::Unexpected(e) => store::GetError::Unexpected(e), - })?; - - let origin = sync::Arc::new(origin); - - let mut origins = self.origins.write().unwrap(); - - (*origins).insert(descr.clone(), origin.clone()); - - Ok(origin) - } - - fn all_descrs(&self) -> Result, store::AllDescrsError> { + fn all_descrs(&self) -> Result, origin::AllDescrsError> { fs::read_dir(&self.dir_path).or_unexpected()?.map( - |dir_entry_res: io::Result| -> Result { + |dir_entry_res: io::Result| -> Result { let descr_id: String = dir_entry_res .or_unexpected()? .file_name() @@ -309,11 +287,55 @@ impl super::Store for FSStore { }, ).try_collect() } + + fn read_file_into( + &self, + descr: &origin::Descr, + path: &str, + into: &mut dyn std::io::Write, + ) -> Result<(), origin::ReadFileIntoError> { + let repo_snapshot = match self.get_repo_snapshot(descr) { + Ok(Some(repo_snapshot)) => repo_snapshot, + Ok(None) => return Err(origin::ReadFileIntoError::DescrNotSynced), + Err(e) => return Err(e.into()), + }; + + let mut clean_path = Path::new(path); + clean_path = clean_path.strip_prefix("/").unwrap_or(clean_path); + + let repo = repo_snapshot.repo.to_thread_local(); + + let file_object = repo + .find_object(repo_snapshot.tree_object_id) + .map_unexpected_while(|| { + format!("finding tree object {}", repo_snapshot.tree_object_id) + })? + .peel_to_tree() + .map_unexpected_while(|| { + format!("peeling tree object {}", repo_snapshot.tree_object_id) + })? + .lookup_entry_by_path(clean_path) + .map_unexpected_while(|| { + format!( + "looking up {} in tree object {}", + clean_path.display(), + repo_snapshot.tree_object_id + ) + })? + .ok_or(origin::ReadFileIntoError::FileNotFound)? + .object() + .or_unexpected()?; + + into.write_all(file_object.data.as_ref()) + .or_unexpected_while("copying out file")?; + + Ok(()) + } } #[cfg(test)] mod tests { - use crate::origin::{self, store, store::Store}; + use crate::origin::{self, Store}; use tempdir::TempDir; #[test] @@ -337,17 +359,10 @@ mod tests { store.sync(&descr).expect("sync should succeed"); store.sync(&descr).expect("second sync should succeed"); - assert!(matches!( - store.get(&other_descr), - Err::<_, store::GetError>(store::GetError::NotFound), - )); - - let origin = store.get(&descr).expect("origin retrieved"); - let assert_write = |path: &str| { let mut into: Vec = vec![]; - origin - .read_file_into(path, &mut into) + store + .read_file_into(&descr, path, &mut into) .expect("write should succeed"); assert!(into.len() > 0); }; @@ -355,10 +370,17 @@ mod tests { assert_write("src/lib.rs"); assert_write("/src/lib.rs"); - // File doesn't exist let mut into: Vec = vec![]; + + // RepoSnapshot doesn't exist assert!(matches!( - origin.read_file_into("DNE", &mut into), + store.read_file_into(&other_descr, "DNE", &mut into), + Err::<_, origin::ReadFileIntoError>(origin::ReadFileIntoError::DescrNotSynced), + )); + + // File doesn't exist + assert!(matches!( + store.read_file_into(&descr, "DNE", &mut into), Err::<(), origin::ReadFileIntoError>(origin::ReadFileIntoError::FileNotFound), )); assert_eq!(into.len(), 0); diff --git a/src/origin/store/mux.rs b/src/origin/mux.rs similarity index 77% rename from src/origin/store/mux.rs rename to src/origin/mux.rs index 4f156e2..f7f1066 100644 --- a/src/origin/store/mux.rs +++ b/src/origin/mux.rs @@ -1,10 +1,9 @@ use crate::error::unexpected::Mappable; -use crate::origin::{self, store}; -use std::sync; +use crate::origin; pub struct Store where - S: store::Store + 'static, + S: origin::Store + 'static, F: Fn(&origin::Descr) -> Option + Sync + Send, { mapping_fn: F, @@ -13,7 +12,7 @@ where impl Store where - S: store::Store + 'static, + S: origin::Store + 'static, F: Fn(&origin::Descr) -> Option + Sync + Send, { pub fn new(mapping_fn: F, stores: Vec) -> Store { @@ -21,24 +20,18 @@ where } } -impl store::Store for Store +impl origin::Store for Store where - S: store::Store + 'static, + S: origin::Store + 'static, F: Fn(&origin::Descr) -> Option + Sync + Send, { - fn sync(&self, descr: &origin::Descr) -> Result<(), store::SyncError> { + fn sync(&self, descr: &origin::Descr) -> Result<(), origin::SyncError> { (self.mapping_fn)(descr) .or_unexpected_while(format!("mapping {:?} to store", &descr))? .sync(descr) } - fn get(&self, descr: &origin::Descr) -> Result, store::GetError> { - (self.mapping_fn)(descr) - .or_unexpected_while(format!("mapping {:?} to store", &descr))? - .get(descr) - } - - fn all_descrs(&self) -> Result, store::AllDescrsError> { + fn all_descrs(&self) -> Result, origin::AllDescrsError> { let mut res = Vec::::new(); for store in self.stores.iter() { @@ -47,20 +40,31 @@ where Ok(res) } + + fn read_file_into( + &self, + descr: &origin::Descr, + path: &str, + into: &mut dyn std::io::Write, + ) -> Result<(), origin::ReadFileIntoError> { + (self.mapping_fn)(descr) + .or_unexpected_while(format!("mapping {:?} to store", &descr))? + .read_file_into(descr, path, into) + } } #[cfg(test)] mod tests { - use crate::origin::{self, store}; + use crate::origin; use std::sync; struct Harness { descr_a: origin::Descr, descr_b: origin::Descr, descr_unknown: origin::Descr, - store_a: sync::Arc>, - store_b: sync::Arc>, - store: Box, + store_a: sync::Arc>, + store_b: sync::Arc>, + store: Box, } impl Harness { @@ -75,8 +79,8 @@ mod tests { branch_name: "B".to_string(), }; - let store_a = store::new_mock(); - let store_b = store::new_mock(); + let store_a = origin::new_mock(); + let store_b = origin::new_mock(); Harness { descr_a: descr_a.clone(), @@ -119,7 +123,7 @@ mod tests { .expect_sync() .withf(move |descr: &origin::Descr| descr == &descr_a) .times(1) - .return_const(Ok::<(), store::SyncError>(())); + .return_const(Ok::<(), origin::SyncError>(())); } assert_eq!(Ok(()), h.store.sync(&h.descr_a)); @@ -132,7 +136,7 @@ mod tests { .expect_sync() .withf(move |descr: &origin::Descr| descr == &descr_b) .times(1) - .return_const(Ok::<(), store::SyncError>(())); + .return_const(Ok::<(), origin::SyncError>(())); } assert_eq!(Ok(()), h.store.sync(&h.descr_b)); @@ -149,7 +153,7 @@ mod tests { .unwrap() .expect_all_descrs() .times(1) - .return_const(Ok::, store::AllDescrsError>(vec![h + .return_const(Ok::, origin::AllDescrsError>(vec![h .descr_a .clone()])); @@ -158,7 +162,7 @@ mod tests { .unwrap() .expect_all_descrs() .times(1) - .return_const(Ok::, store::AllDescrsError>(vec![h + .return_const(Ok::, origin::AllDescrsError>(vec![h .descr_b .clone()])); diff --git a/src/origin/store.rs b/src/origin/store.rs deleted file mode 100644 index a420ca4..0000000 --- a/src/origin/store.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::error::unexpected; -use crate::origin; -use std::sync; - -pub mod git; -pub mod mux; - -#[derive(thiserror::Error, Clone, Debug, PartialEq)] -pub enum SyncError { - #[error("invalid url")] - InvalidURL, - - #[error("invalid branch name")] - InvalidBranchName, - - #[error("already in progress")] - AlreadyInProgress, - - #[error(transparent)] - Unexpected(#[from] unexpected::Error), -} - -#[derive(thiserror::Error, Clone, Debug, PartialEq)] -pub enum GetError { - #[error("not found")] - NotFound, - - #[error(transparent)] - Unexpected(#[from] unexpected::Error), -} - -#[derive(thiserror::Error, Clone, Debug, PartialEq)] -pub enum AllDescrsError { - #[error(transparent)] - Unexpected(#[from] unexpected::Error), -} - -#[mockall::automock] -/// 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: &origin::Descr) -> Result<(), SyncError>; - - fn get(&self, descr: &origin::Descr) -> Result, GetError>; - fn all_descrs(&self) -> Result, AllDescrsError>; -} - -pub fn new_mock() -> sync::Arc> { - sync::Arc::new(sync::Mutex::new(MockStore::new())) -} - -impl Store for sync::Arc> { - fn sync(&self, descr: &origin::Descr) -> Result<(), SyncError> { - self.lock().unwrap().sync(descr) - } - - fn get(&self, descr: &origin::Descr) -> Result, GetError> { - self.lock().unwrap().get(descr) - } - - fn all_descrs(&self) -> Result, AllDescrsError> { - self.lock().unwrap().all_descrs() - } -} diff --git a/src/service/http.rs b/src/service/http.rs index cb286b9..0f9e1b3 100644 --- a/src/service/http.rs +++ b/src/service/http.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use std::{future, net, sync}; use crate::error::unexpected; -use crate::{domain, origin, service, util}; +use crate::{domain, service, util}; type SvcResponse = Result, String>; @@ -170,23 +170,16 @@ impl<'svc> Service { false => path, }; - let origin = match self.domain_manager.get_origin(&domain) { - Ok(o) => o, - Err(domain::manager::GetOriginError::NotFound) => { + let mut buf = Vec::::new(); + match self.domain_manager.read_file_into(&domain, path, &mut buf) { + Ok(_) => self.serve_string(200, path, buf), + Err(domain::manager::ReadFileIntoError::DomainNotFound) => { return self.render_error_page(404, "Domain not found") } - Err(domain::manager::GetOriginError::Unexpected(e)) => { - return self.render_error_page(500, format!("failed to fetch origin: {e}").as_str()) - } - }; - - let mut buf = Vec::::new(); - match origin.read_file_into(path, &mut buf) { - Ok(_) => self.serve_string(200, path, buf), - Err(origin::ReadFileIntoError::FileNotFound) => { + Err(domain::manager::ReadFileIntoError::FileNotFound) => { self.render_error_page(404, "File not found") } - Err(origin::ReadFileIntoError::Unexpected(e)) => { + Err(domain::manager::ReadFileIntoError::Unexpected(e)) => { self.render_error_page(500, format!("failed to fetch file {path}: {e}").as_str()) } }