witchcraft_server/
witchcraft.rsuse crate::blocking::conjure::ConjureBlockingEndpoint;
use crate::blocking::pool::ThreadPool;
use crate::debug::DiagnosticRegistry;
use crate::endpoint::conjure::ConjureEndpoint;
use crate::endpoint::extended_path::ExtendedPathEndpoint;
use crate::endpoint::WitchcraftEndpoint;
use crate::health::HealthCheckRegistry;
use crate::readiness::ReadinessCheckRegistry;
use crate::shutdown_hooks::ShutdownHooks;
use crate::{blocking, RequestBody, ResponseWriter};
use conjure_http::server::{AsyncService, BoxAsyncEndpoint, ConjureRuntime, Endpoint, Service};
use conjure_runtime::ClientFactory;
use futures_util::Future;
use std::sync::Arc;
use tokio::runtime::Handle;
use witchcraft_metrics::MetricRegistry;
use witchcraft_server_config::install::InstallConfig;
pub struct Witchcraft {
pub(crate) metrics: Arc<MetricRegistry>,
pub(crate) health_checks: Arc<HealthCheckRegistry>,
pub(crate) readiness_checks: Arc<ReadinessCheckRegistry>,
pub(crate) diagnostics: Arc<DiagnosticRegistry>,
pub(crate) client_factory: ClientFactory,
pub(crate) handle: Handle,
pub(crate) install_config: InstallConfig,
pub(crate) thread_pool: Option<Arc<ThreadPool>>,
pub(crate) endpoints: Vec<Box<dyn WitchcraftEndpoint + Sync + Send>>,
pub(crate) shutdown_hooks: ShutdownHooks,
pub(crate) conjure_runtime: Arc<ConjureRuntime>,
}
impl Witchcraft {
#[inline]
pub fn metrics(&self) -> &Arc<MetricRegistry> {
&self.metrics
}
#[inline]
pub fn health_checks(&self) -> &Arc<HealthCheckRegistry> {
&self.health_checks
}
#[inline]
pub fn readiness_checks(&self) -> &Arc<ReadinessCheckRegistry> {
&self.readiness_checks
}
#[inline]
pub fn client_factory(&self) -> &ClientFactory {
&self.client_factory
}
#[inline]
pub fn diagnostics(&self) -> &Arc<DiagnosticRegistry> {
&self.diagnostics
}
#[inline]
pub fn handle(&self) -> &Handle {
&self.handle
}
pub fn app<T>(&mut self, service: T)
where
T: AsyncService<RequestBody, ResponseWriter>,
{
self.endpoints(None, service.endpoints(&self.conjure_runtime), true)
}
pub fn api<T>(&mut self, service: T)
where
T: AsyncService<RequestBody, ResponseWriter>,
{
self.endpoints(Some("/api"), service.endpoints(&self.conjure_runtime), true)
}
pub(crate) fn endpoints(
&mut self,
prefix: Option<&str>,
endpoints: Vec<BoxAsyncEndpoint<'static, RequestBody, ResponseWriter>>,
track_metrics: bool,
) {
let metrics = if track_metrics {
Some(&*self.metrics)
} else {
None
};
self.endpoints.extend(
endpoints
.into_iter()
.map(|e| Box::new(ConjureEndpoint::new(metrics, e)))
.map(|e| extend_path(e, self.install_config.context_path(), prefix)),
)
}
pub fn blocking_app<T>(&mut self, service: T)
where
T: Service<blocking::RequestBody, blocking::ResponseWriter>,
{
self.blocking_endpoints(None, service.endpoints(&self.conjure_runtime))
}
pub fn blocking_api<T>(&mut self, service: T)
where
T: Service<blocking::RequestBody, blocking::ResponseWriter>,
{
self.blocking_endpoints(Some("/api"), service.endpoints(&self.conjure_runtime))
}
fn blocking_endpoints(
&mut self,
prefix: Option<&str>,
endpoints: Vec<
Box<dyn Endpoint<blocking::RequestBody, blocking::ResponseWriter> + Sync + Send>,
>,
) {
let thread_pool = self
.thread_pool
.get_or_insert_with(|| Arc::new(ThreadPool::new(&self.install_config, &self.metrics)));
self.endpoints.extend(
endpoints
.into_iter()
.map(|e| Box::new(ConjureBlockingEndpoint::new(&self.metrics, thread_pool, e)))
.map(|e| extend_path(e, self.install_config.context_path(), prefix)),
)
}
pub fn on_shutdown<F>(&mut self, future: F)
where
F: Future<Output = ()> + 'static + Send,
{
self.shutdown_hooks.push(future)
}
}
fn extend_path(
endpoint: Box<dyn WitchcraftEndpoint + Sync + Send>,
context_path: &str,
prefix: Option<&str>,
) -> Box<dyn WitchcraftEndpoint + Sync + Send> {
let context_path = if context_path == "/" {
""
} else {
context_path
};
let prefix = format!("{context_path}{}", prefix.unwrap_or(""));
if prefix.is_empty() {
endpoint
} else {
Box::new(ExtendedPathEndpoint::new(endpoint, &prefix))
}
}