use crate::facade::config::initial::statics::StaticInitialConfig;
use crate::AppConfigError;
use config::{ConfigBuilder, ConfigError};
use serde::de::{DeserializeOwned, MapAccess, Visitor};
use serde::{Deserialize, Deserializer};
use std::fmt::Formatter;
use std::sync::Arc;
use strut_factory::impl_deserialize_field;
#[cfg(feature = "config-async")]
use config::builder::AsyncState;
#[cfg(not(feature = "config-async"))]
use config::builder::DefaultState;
pub mod statics;
#[derive(Debug, Clone)]
pub struct AppConfig {
name: Arc<str>,
#[cfg(feature = "tracing")]
tracing: strut_tracing::TracingConfig,
#[cfg(feature = "sentry")]
sentry: strut_sentry::SentryConfig,
#[cfg(any(
feature = "database-mysql",
feature = "database-postgres",
feature = "database-sqlite",
))]
database: strut_database::DatabaseConfig,
#[cfg(feature = "rabbitmq")]
rabbitmq: strut_rabbitmq::RabbitMqConfig,
}
impl AppConfig {
pub fn get() -> &'static Self {
StaticInitialConfig::app_config()
}
pub fn section<T>(key: impl AsRef<str>) -> T
where
T: DeserializeOwned + Default,
{
let key = key.as_ref();
Self::try_section(key).unwrap_or_else(|error| {
panic!(
"failed to load or parse the application configuration section '{}': {}",
key, error,
);
})
}
pub fn try_section<T>(key: impl AsRef<str>) -> Result<T, AppConfigError>
where
T: DeserializeOwned + Default,
{
StaticInitialConfig::proxy_config()
.get(key.as_ref())
.or_else(|error| match error {
ConfigError::NotFound(_) => Ok(T::default()),
_ => Err(AppConfigError::from(error)),
})
}
}
#[cfg(not(feature = "config-async"))]
impl AppConfig {
pub fn seed(config_builder: ConfigBuilder<DefaultState>) {
let proxy_config = config_builder
.build()
.expect("it should be possible to synchronously build the initial proxy configuration");
StaticInitialConfig::seed(proxy_config);
}
}
#[cfg(feature = "config-async")]
impl AppConfig {
pub async fn seed(config_builder: ConfigBuilder<AsyncState>) {
let proxy_config = config_builder.build().await.expect(
"it should be possible to asynchronously build the initial proxy configuration",
);
StaticInitialConfig::seed(proxy_config);
}
}
impl AppConfig {
pub fn name(&self) -> &str {
&self.name
}
#[cfg(feature = "tracing")]
pub fn tracing(&self) -> &strut_tracing::TracingConfig {
&self.tracing
}
#[cfg(feature = "sentry")]
pub fn sentry(&self) -> &strut_sentry::SentryConfig {
&self.sentry
}
#[cfg(any(
feature = "database-mysql",
feature = "database-postgres",
feature = "database-sqlite",
))]
pub fn database(&self) -> &strut_database::DatabaseConfig {
&self.database
}
#[cfg(feature = "rabbitmq")]
pub fn rabbitmq(&self) -> &strut_rabbitmq::RabbitMqConfig {
&self.rabbitmq
}
}
impl AppConfig {
fn default_name() -> &'static str {
"app"
}
}
const _: () = {
impl<'de> Deserialize<'de> for AppConfig {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(AppConfigVisitor)
}
}
struct AppConfigVisitor;
impl<'de> Visitor<'de> for AppConfigVisitor {
type Value = AppConfig;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("a map of application configuration")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut name: Option<String> = None;
#[cfg(feature = "tracing")]
let mut tracing = None;
#[cfg(feature = "sentry")]
let mut sentry = None;
#[cfg(any(
feature = "database-mysql",
feature = "database-postgres",
feature = "database-sqlite",
))]
let mut database = None;
#[cfg(feature = "rabbitmq")]
let mut rabbitmq = None;
while let Some(key) = map.next_key()? {
match key {
AppConfigField::name => key.poll(&mut map, &mut name)?,
#[cfg(feature = "tracing")]
AppConfigField::tracing => key.poll(&mut map, &mut tracing)?,
#[cfg(not(feature = "tracing"))]
AppConfigField::tracing => map.next_value()?,
#[cfg(feature = "sentry")]
AppConfigField::sentry => key.poll(&mut map, &mut sentry)?,
#[cfg(not(feature = "sentry"))]
AppConfigField::sentry => map.next_value()?,
#[cfg(any(
feature = "database-mysql",
feature = "database-postgres",
feature = "database-sqlite",
))]
AppConfigField::database => key.poll(&mut map, &mut database)?,
#[cfg(not(any(
feature = "database-mysql",
feature = "database-postgres",
feature = "database-sqlite",
)))]
AppConfigField::database => map.next_value()?,
#[cfg(feature = "rabbitmq")]
AppConfigField::rabbitmq => key.poll(&mut map, &mut rabbitmq)?,
#[cfg(not(feature = "rabbitmq"))]
AppConfigField::rabbitmq => map.next_value()?,
AppConfigField::__ignore => map.next_value()?,
};
}
let name = Arc::from(name.as_deref().unwrap_or_else(|| AppConfig::default_name()));
Ok(AppConfig {
name,
#[cfg(feature = "tracing")]
tracing: tracing.unwrap_or_default(),
#[cfg(feature = "sentry")]
sentry: sentry.unwrap_or_default(),
#[cfg(any(
feature = "database-mysql",
feature = "database-postgres",
feature = "database-sqlite",
))]
database: database.unwrap_or_default(),
#[cfg(feature = "rabbitmq")]
rabbitmq: rabbitmq.unwrap_or_default(),
})
}
}
impl_deserialize_field!(
AppConfigField,
strut_deserialize::Slug::eq_as_slugs,
name,
tracing,
sentry,
rabbitmq,
database
);
};