use std::{collections::HashMap, marker::PhantomData};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::{
apidoc::ApiDocBuilder,
auth::AuthConfig,
builder::server::ServerBuilder,
http_client::HttpClientConfig,
layers::{
buffer::HandlerBufferConfig, cors::CorsConfig, rate::HandlerRateLimitConfig,
timeout::HandlerTimeoutConfig,
},
logging::LoggingConfig,
metrics::{MetricsBuilder, MetricsState},
probes::ProbeConfig,
runtime::RuntimeConfig,
telemetry::TelemetryConfig,
tracing::TracingConfig,
};
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[non_exhaustive]
pub struct ServiceConfig<C = ()>
where
C: Clone + std::fmt::Debug,
{
#[serde(flatten)]
pub app: AppConfig,
#[serde(default)]
pub server: ServerBuilder,
#[serde(flatten)]
pub service: C,
}
impl<C> ServiceConfig<C>
where
C: Clone + std::fmt::Debug,
{
pub fn builder() -> ServiceConfigBuilder<C> {
ServiceConfigBuilder::new()
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ServiceConfigError {
#[error(transparent)]
Config(#[from] config::ConfigError),
}
#[must_use]
pub struct ServiceConfigBuilder<C>
where
C: Clone + std::fmt::Debug,
{
builder: config::ConfigBuilder<config::builder::DefaultState>,
_type: PhantomData<C>,
}
impl<C> ServiceConfigBuilder<C>
where
C: Clone + std::fmt::Debug,
{
pub fn new() -> Self {
Self {
builder: config::Config::builder(),
_type: PhantomData,
}
}
}
impl<C> Default for ServiceConfigBuilder<C>
where
C: Clone + std::fmt::Debug,
{
fn default() -> Self {
Self::new()
}
}
impl<C> ServiceConfigBuilder<C>
where
C: Clone + std::fmt::Debug + for<'de> Deserialize<'de>,
{
pub fn build(self) -> Result<ServiceConfig<C>, ServiceConfigError> {
self.builder.build()?.try_deserialize().map_err(Into::into)
}
pub fn with_source<T>(mut self, source: T) -> Self
where
T: config::Source + Send + Sync + 'static,
{
self.builder = self.builder.add_source(source);
self
}
pub fn with_file(self, name: impl AsRef<str>) -> Self {
self.with_source(config::File::with_name(name.as_ref()))
}
pub fn with_env(self, prefix: impl AsRef<str>) -> Self {
self.with_source(
config::Environment::with_prefix(prefix.as_ref())
.separator("__")
.prefix_separator("__"),
)
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[non_exhaustive]
pub struct AppConfig {
#[serde(default)]
pub runtime: RuntimeConfig,
#[serde(default)]
pub logging: LoggingConfig,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tracing: Option<TracingConfig>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub handlers: HashMap<String, HandlerConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub api_doc: Option<ApiDocBuilder>,
#[serde(
default = "AppConfig::default_metrics",
skip_serializing_if = "Option::is_none"
)]
pub metrics: Option<MetricsBuilder>,
#[serde(default)]
pub probes: ProbeConfig,
#[serde(default)]
pub auth: AuthConfig,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub http_clients: HashMap<String, HttpClientConfig>,
#[serde(default)]
pub telemetry: TelemetryConfig,
#[serde(skip)]
pub app_name: Option<String>,
#[serde(skip)]
pub app_version: Option<String>,
#[serde(skip)]
pub metrics_state: Option<MetricsState>,
}
impl AppConfig {
#[must_use]
#[inline]
fn default_metrics() -> Option<MetricsBuilder> {
Some(MetricsBuilder::default())
}
pub fn with_app_name(&mut self, app_name: impl ToString) -> &mut Self {
self.app_name = Some(app_name.to_string());
self
}
pub fn with_app_version(&mut self, app_version: impl ToString) -> &mut Self {
self.app_version = Some(app_version.to_string());
self
}
pub fn with_metrics_state(&mut self, metrics_state: MetricsState) -> &mut Self {
self.metrics_state = Some(metrics_state);
self
}
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[non_exhaustive]
pub struct HandlerConfig {
#[serde(default)]
pub disabled: bool,
#[serde(default)]
pub hidden: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub buffer: Option<HandlerBufferConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cors: Option<CorsConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rate_limit: Option<HandlerRateLimitConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub throttle: Option<u8>,
#[serde(default, skip_serializing_if = "HandlerTimeoutConfig::is_default")]
pub timeout: HandlerTimeoutConfig,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub permissions: Vec<String>,
}