Introduced separate 'Boxed' traits, to allow for mocks

This commit is contained in:
Brian Picciano 2023-05-16 17:17:47 +02:00
parent d9676a4ce7
commit cab7a837a7
6 changed files with 78 additions and 38 deletions

View File

@ -1,6 +1,6 @@
use std::error::Error; use std::error::Error;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::{fs, io}; use std::{fs, io, sync};
use crate::domain; use crate::domain;
use crate::origin::Descr; use crate::origin::Descr;
@ -39,20 +39,22 @@ pub enum SetError {
} }
#[mockall::automock] #[mockall::automock]
pub trait Store: std::marker::Send + std::marker::Sync { pub trait Store {
fn get(&self, domain: &domain::Name) -> Result<Config, GetError>; fn get(&self, domain: &domain::Name) -> Result<Config, GetError>;
fn set(&self, domain: &domain::Name, config: &Config) -> Result<(), SetError>; fn set(&self, domain: &domain::Name, config: &Config) -> Result<(), SetError>;
} }
pub trait BoxedStore: Store + Send + Sync + Clone {}
struct FSStore { struct FSStore {
dir_path: PathBuf, dir_path: PathBuf,
} }
pub fn new(dir_path: &Path) -> io::Result<impl Store> { pub fn new(dir_path: &Path) -> io::Result<impl BoxedStore> {
fs::create_dir_all(dir_path)?; fs::create_dir_all(dir_path)?;
Ok(FSStore { Ok(sync::Arc::new(FSStore {
dir_path: dir_path.into(), dir_path: dir_path.into(),
}) }))
} }
impl FSStore { impl FSStore {
@ -65,7 +67,9 @@ impl FSStore {
} }
} }
impl Store for FSStore { impl BoxedStore for sync::Arc<FSStore> {}
impl Store for sync::Arc<FSStore> {
fn get(&self, domain: &domain::Name) -> Result<Config, GetError> { fn get(&self, domain: &domain::Name) -> Result<Config, GetError> {
let config_file = let config_file =
fs::File::open(self.config_file_path(domain)).map_err(|e| match e.kind() { fs::File::open(self.config_file_path(domain)).map_err(|e| match e.kind() {

View File

@ -2,7 +2,7 @@ use crate::domain::{self, checker, config};
use crate::origin; use crate::origin;
use std::error::Error; use std::error::Error;
use std::future::Future; use std::future::Future;
use std::pin; use std::{pin, sync};
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum GetConfigError { pub enum GetConfigError {
@ -113,8 +113,8 @@ impl From<config::SetError> for SyncWithConfigError {
} }
} }
//#[mockall::automock(type Origin=origin::MockOrigin;)] //#[mockall::automock]
pub trait Manager: Send + Sync { pub trait Manager {
fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError>; fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError>;
fn get_origin( fn get_origin(
&self, &self,
@ -128,36 +128,47 @@ pub trait Manager: Send + Sync {
) -> pin::Pin<Box<dyn Future<Output = Result<(), SyncWithConfigError>> + Send + '_>>; ) -> pin::Pin<Box<dyn Future<Output = Result<(), SyncWithConfigError>> + Send + '_>>;
} }
pub trait BoxedManager: Manager + Send + Sync + Clone {}
struct ManagerImpl<OriginStore, DomainConfigStore>
where
OriginStore: origin::store::BoxedStore,
DomainConfigStore: config::BoxedStore,
{
origin_store: OriginStore,
domain_config_store: DomainConfigStore,
domain_checker: checker::DNSChecker,
}
pub fn new<OriginStore, DomainConfigStore>( pub fn new<OriginStore, DomainConfigStore>(
origin_store: OriginStore, origin_store: OriginStore,
domain_config_store: DomainConfigStore, domain_config_store: DomainConfigStore,
domain_checker: checker::DNSChecker, domain_checker: checker::DNSChecker,
) -> impl Manager ) -> impl BoxedManager
where where
OriginStore: origin::store::Store, OriginStore: origin::store::BoxedStore,
DomainConfigStore: config::Store, DomainConfigStore: config::BoxedStore,
{ {
ManagerImpl { sync::Arc::new(ManagerImpl {
origin_store, origin_store,
domain_config_store, domain_config_store,
domain_checker, domain_checker,
} })
} }
struct ManagerImpl<OriginStore, DomainConfigStore> impl<OriginStore, DomainConfigStore> BoxedManager
for sync::Arc<ManagerImpl<OriginStore, DomainConfigStore>>
where where
OriginStore: origin::store::Store, OriginStore: origin::store::BoxedStore,
DomainConfigStore: config::Store, DomainConfigStore: config::BoxedStore,
{ {
origin_store: OriginStore,
domain_config_store: DomainConfigStore,
domain_checker: checker::DNSChecker,
} }
impl<OriginStore, DomainConfigStore> Manager for ManagerImpl<OriginStore, DomainConfigStore> impl<OriginStore, DomainConfigStore> Manager
for sync::Arc<ManagerImpl<OriginStore, DomainConfigStore>>
where where
OriginStore: origin::store::Store, OriginStore: origin::store::BoxedStore,
DomainConfigStore: config::Store, DomainConfigStore: config::BoxedStore,
{ {
fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError> { fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError> {
Ok(self.domain_config_store.get(domain)?) Ok(self.domain_config_store.get(domain)?)

View File

@ -123,7 +123,6 @@ fn main() {
.expect("domain config store initialized"); .expect("domain config store initialized");
let manager = domiply::domain::manager::new(origin_store, domain_config_store, domain_checker); let manager = domiply::domain::manager::new(origin_store, domain_config_store, domain_checker);
let manager = sync::Arc::new(manager);
let service = domiply::service::new( let service = domiply::service::new(
manager, manager,

View File

@ -41,13 +41,17 @@ pub enum AllDescrsError {
/// Used in the return from all_descrs from Store. /// Used in the return from all_descrs from Store.
pub type AllDescrsResult<T> = Result<T, AllDescrsError>; pub type AllDescrsResult<T> = Result<T, AllDescrsError>;
#[mockall::automock(
type Origin=origin::MockOrigin;
type AllDescrsIter=Vec<AllDescrsResult<origin::Descr>>;
)]
/// Describes a storage mechanism for Origins. Each Origin is uniquely identified by its Descr. /// Describes a storage mechanism for Origins. Each Origin is uniquely identified by its Descr.
pub trait Store: Send + Sync + Clone { pub trait Store {
type Origin<'store>: origin::Origin + 'store type Origin<'store>: origin::Origin + 'store
where where
Self: 'store; Self: 'store;
type AllDescrsIter<'store>: IntoIterator<Item = AllDescrsResult<origin::Descr>> + Send + 'store type AllDescrsIter<'store>: IntoIterator<Item = AllDescrsResult<origin::Descr>> + 'store
where where
Self: 'store; Self: 'store;
@ -58,3 +62,5 @@ pub trait Store: Send + Sync + Clone {
fn get(&self, descr: origin::Descr) -> Result<Self::Origin<'_>, GetError>; fn get(&self, descr: origin::Descr) -> Result<Self::Origin<'_>, GetError>;
fn all_descrs(&self) -> AllDescrsResult<Self::AllDescrsIter<'_>>; fn all_descrs(&self) -> AllDescrsResult<Self::AllDescrsIter<'_>>;
} }
pub trait BoxedStore: Store + Send + Sync + Clone {}

View File

@ -65,7 +65,7 @@ struct Store {
origins: sync::RwLock<collections::HashMap<origin::Descr, sync::Arc<Origin>>>, origins: sync::RwLock<collections::HashMap<origin::Descr, sync::Arc<Origin>>>,
} }
pub fn new(dir_path: PathBuf) -> io::Result<impl super::Store> { pub fn new(dir_path: PathBuf) -> io::Result<impl super::BoxedStore> {
fs::create_dir_all(&dir_path)?; fs::create_dir_all(&dir_path)?;
Ok(sync::Arc::new(Store { Ok(sync::Arc::new(Store {
dir_path, dir_path,
@ -191,11 +191,13 @@ impl Store {
} }
} }
impl super::BoxedStore for sync::Arc<Store> {}
impl super::Store for sync::Arc<Store> { impl super::Store for sync::Arc<Store> {
type Origin<'store> = sync::Arc<Origin> type Origin<'store> = sync::Arc<Origin>
where Self: 'store; where Self: 'store;
type AllDescrsIter<'store> = Box<dyn Iterator<Item = store::AllDescrsResult<origin::Descr>> + Send + 'store> type AllDescrsIter<'store> = Box<dyn Iterator<Item = store::AllDescrsResult<origin::Descr>> + 'store>
where Self: 'store; where Self: 'store;
fn sync(&self, descr: origin::Descr, limits: store::Limits) -> Result<(), store::SyncError> { fn sync(&self, descr: origin::Descr, limits: store::Limits) -> Result<(), store::SyncError> {

View File

@ -15,20 +15,26 @@ mod util;
type SvcResponse = Result<Response<hyper::body::Body>, String>; type SvcResponse = Result<Response<hyper::body::Body>, String>;
#[derive(Clone)] #[derive(Clone)]
pub struct Service<'svc> { pub struct Service<'svc, DomainManager>
domain_manager: sync::Arc<dyn domain::manager::Manager>, where
DomainManager: domain::manager::BoxedManager,
{
domain_manager: DomainManager,
target_a: net::Ipv4Addr, target_a: net::Ipv4Addr,
passphrase: String, passphrase: String,
http_domain: String, http_domain: String,
handlebars: handlebars::Handlebars<'svc>, handlebars: handlebars::Handlebars<'svc>,
} }
pub fn new<'svc>( pub fn new<'svc, DomainManager>(
domain_manager: sync::Arc<dyn domain::manager::Manager>, domain_manager: DomainManager,
target_a: net::Ipv4Addr, target_a: net::Ipv4Addr,
passphrase: String, passphrase: String,
http_domain: String, http_domain: String,
) -> Service<'svc> { ) -> Service<'svc, DomainManager>
where
DomainManager: domain::manager::BoxedManager,
{
Service { Service {
domain_manager, domain_manager,
target_a, target_a,
@ -60,7 +66,10 @@ struct DomainSyncArgs {
passphrase: String, passphrase: String,
} }
impl<'svc> Service<'svc> { impl<'svc, DomainManager> Service<'svc, DomainManager>
where
DomainManager: domain::manager::BoxedManager,
{
fn serve_string(&self, status_code: u16, path: &'_ str, body: Vec<u8>) -> SvcResponse { fn serve_string(&self, status_code: u16, path: &'_ str, body: Vec<u8>) -> SvcResponse {
let content_type = mime_guess::from_path(path) let content_type = mime_guess::from_path(path)
.first_or_octet_stream() .first_or_octet_stream()
@ -280,10 +289,13 @@ impl<'svc> Service<'svc> {
} }
} }
pub async fn handle_request( pub async fn handle_request<DomainManager>(
svc: sync::Arc<Service<'_>>, svc: sync::Arc<Service<'_, DomainManager>>,
req: Request<Body>, req: Request<Body>,
) -> Result<Response<Body>, Infallible> { ) -> Result<Response<Body>, Infallible>
where
DomainManager: domain::manager::BoxedManager,
{
match handle_request_inner(svc, req).await { match handle_request_inner(svc, req).await {
Ok(res) => Ok(res), Ok(res) => Ok(res),
Err(err) => panic!("unexpected error {err}"), Err(err) => panic!("unexpected error {err}"),
@ -297,7 +309,13 @@ fn strip_port(host: &str) -> &str {
} }
} }
pub async fn handle_request_inner(svc: sync::Arc<Service<'_>>, req: Request<Body>) -> SvcResponse { pub async fn handle_request_inner<DomainManager>(
svc: sync::Arc<Service<'_, DomainManager>>,
req: Request<Body>,
) -> SvcResponse
where
DomainManager: domain::manager::BoxedManager,
{
let maybe_host = match ( let maybe_host = match (
req.headers() req.headers()
.get("Host") .get("Host")