use crate::domain::{self, checker, config}; use crate::origin; use std::error::Error; #[derive(thiserror::Error, Debug)] pub enum GetConfigError { #[error("not found")] NotFound, #[error(transparent)] Unexpected(Box), } impl From for GetConfigError { fn from(e: config::GetError) -> GetConfigError { match e { config::GetError::NotFound => GetConfigError::NotFound, config::GetError::Unexpected(e) => GetConfigError::Unexpected(e), } } } #[derive(thiserror::Error, Debug)] pub enum GetOriginError { #[error("not found")] NotFound, #[error(transparent)] Unexpected(Box), } impl From for GetOriginError { fn from(e: config::GetError) -> GetOriginError { match e { config::GetError::NotFound => GetOriginError::NotFound, config::GetError::Unexpected(e) => GetOriginError::Unexpected(e), } } } #[derive(thiserror::Error, Debug)] pub enum SyncError { #[error("not found")] NotFound, #[error("already in progress")] AlreadyInProgress, #[error(transparent)] Unexpected(Box), } impl From for SyncError { fn from(e: config::GetError) -> SyncError { match e { config::GetError::NotFound => SyncError::NotFound, config::GetError::Unexpected(e) => SyncError::Unexpected(e), } } } #[derive(thiserror::Error, Debug)] pub enum SyncWithConfigError { #[error("invalid url")] InvalidURL, #[error("invalid branch name")] InvalidBranchName, #[error("already in progress")] AlreadyInProgress, #[error("target CNAME not set")] TargetCNAMENotSet, #[error("challenge token not set")] ChallengeTokenNotSet, #[error(transparent)] Unexpected(Box), } impl From for SyncWithConfigError { fn from(e: origin::store::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), } } } impl From for SyncWithConfigError { fn from(e: checker::CheckDomainError) -> SyncWithConfigError { match e { checker::CheckDomainError::TargetCNAMENotSet => SyncWithConfigError::TargetCNAMENotSet, checker::CheckDomainError::ChallengeTokenNotSet => { SyncWithConfigError::ChallengeTokenNotSet } checker::CheckDomainError::Unexpected(e) => SyncWithConfigError::Unexpected(e), } } } impl From for SyncWithConfigError { fn from(e: config::SetError) -> SyncWithConfigError { match e { config::SetError::Unexpected(e) => SyncWithConfigError::Unexpected(e), } } } #[mockall::automock(type Origin=origin::MockOrigin;)] pub trait Manager: std::marker::Send + std::marker::Sync { type Origin<'mgr>: origin::Origin + 'mgr where Self: 'mgr; fn get_config(&self, domain: &domain::Name) -> Result; fn get_origin(&self, domain: &domain::Name) -> Result, GetOriginError>; fn sync(&self, domain: &domain::Name) -> Result<(), SyncError>; fn sync_with_config( &self, domain: &domain::Name, config: &config::Config, ) -> Result<(), SyncWithConfigError>; } pub fn new( origin_store: OriginStore, domain_config_store: DomainConfigStore, domain_checker: DomainChecker, ) -> impl Manager where OriginStore: origin::store::Store, DomainConfigStore: config::Store, DomainChecker: checker::Checker, { ManagerImpl { origin_store, domain_config_store, domain_checker, } } struct ManagerImpl where OriginStore: origin::store::Store, DomainConfigStore: config::Store, DomainChecker: checker::Checker, { origin_store: OriginStore, domain_config_store: DomainConfigStore, domain_checker: DomainChecker, } impl Manager for ManagerImpl where OriginStore: origin::store::Store, DomainConfigStore: config::Store, DomainChecker: checker::Checker, { type Origin<'mgr> = OriginStore::Origin<'mgr> where Self: 'mgr; fn get_config(&self, domain: &domain::Name) -> Result { Ok(self.domain_config_store.get(domain)?) } fn get_origin(&self, domain: &domain::Name) -> Result, GetOriginError> { 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 .map_err(|e| GetOriginError::Unexpected(Box::from(e)))?; Ok(origin) } fn sync(&self, domain: &domain::Name) -> Result<(), SyncError> { let config = self.domain_config_store.get(domain)?; self.origin_store .sync(config.origin_descr, origin::store::Limits {}) .map_err(|e| match e { origin::store::SyncError::AlreadyInProgress => SyncError::AlreadyInProgress, _ => SyncError::Unexpected(Box::from(e)), })?; Ok(()) } fn sync_with_config( &self, domain: &domain::Name, config: &config::Config, ) -> Result<(), SyncWithConfigError> { let config_hash = config .hash() .map_err(|e| SyncWithConfigError::Unexpected(Box::from(e)))?; self.domain_checker.check_domain(domain, &config_hash)?; self.origin_store .sync(config.origin_descr.clone(), origin::store::Limits {})?; self.domain_config_store.set(domain, config)?; Ok(()) } }