use crate::{path, settings, Builder, Reloadable, Section, Settings};
use arc_swap::ArcSwap;
use std::collections::VecDeque;
use std::fmt::{self, Debug, Display, Formatter, Write};
use std::{any::Any, sync::Arc};
use tokens::{ChangeToken, CompositeChangeToken, Registration, SharedChangeToken, SingleChangeToken};
use tracing::{error, trace};
#[cfg(feature = "binder")]
use {crate::prelude::Binder, serde::de::DeserializeOwned, std::str::FromStr};
#[derive(Clone)]
pub struct Configuration {
pub(crate) settings: Settings,
token: SharedChangeToken<CompositeChangeToken>,
pub(crate) providers: Vec<String>,
}
impl Configuration {
#[inline]
pub fn new(
settings: Settings,
tokens: impl IntoIterator<Item = Box<dyn ChangeToken>>,
providers: Vec<String>,
) -> Self {
Self {
settings,
token: SharedChangeToken::new(CompositeChangeToken::new(tokens.into_iter())),
providers,
}
}
#[inline]
pub fn get(&self, key: &str) -> Option<&str> {
self.settings.get(key)
}
#[inline]
pub fn section(&self, key: impl Into<String>) -> Section<'_> {
Section::new(self, key.into())
}
pub fn sections(&self) -> Vec<Section<'_>> {
let mut keys = Vec::new();
for (path, _) in self {
let Some(key) = path::next(path, None) else {
continue;
};
if !keys.iter().any(|k: &String| k.eq_ignore_ascii_case(key)) {
keys.push(key.to_owned());
}
}
keys.into_iter().map(|key| self.section(key)).collect()
}
#[inline]
pub fn change_token(&self) -> impl ChangeToken {
self.token.clone()
}
}
impl<'a> IntoIterator for &'a Configuration {
type Item = (&'a str, &'a str);
type IntoIter = settings::Iter<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
(&self.settings).into_iter()
}
}
impl IntoIterator for Configuration {
type Item = (String, String);
type IntoIter = settings::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.settings.into_iter()
}
}
impl<'a> From<&'a Configuration> for Vec<Section<'a>> {
#[inline]
fn from(config: &'a Configuration) -> Self {
config.sections()
}
}
impl Debug for Configuration {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.settings, f)
}
}
impl Display for Configuration {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut sections: VecDeque<_> = self.sections().into_iter().map(|s| (0, s)).collect();
while let Some((depth, section)) = sections.pop_front() {
write!(f, "{:width$}", "", width = depth * 2)?;
Display::fmt(§ion, f)?;
for (i, child) in section.sections().into_iter().map(|s| (depth + 1, s)).enumerate() {
sections.insert(i, child);
}
if !sections.is_empty() {
f.write_char('\n')?;
}
}
Ok(())
}
}
fn on_changed(state: Option<Arc<dyn Any + Send + Sync + 'static>>) {
let Some(state) = state else {
return;
};
let inner = state.downcast_ref::<Inner>().expect("received state other than Inner");
match inner.builder.build() {
Ok(config) => {
let registration = inner
.config
.load()
.change_token()
.register(Box::new(on_changed), Some(state.clone()));
let token = inner.token.swap(Arc::new(SharedChangeToken::default()));
inner.config.store(Arc::new(config));
inner.registration.store(Arc::new(registration));
trace!("Reloaded the configuration");
token.notify();
}
Err(error) => error!("Failed to reload the configuration. {error:?}"),
}
}
struct Inner {
builder: Builder,
config: ArcSwap<Configuration>,
registration: ArcSwap<Registration>,
token: ArcSwap<SharedChangeToken<SingleChangeToken>>,
}
pub struct ReloadableConfiguration(Arc<Inner>);
impl ReloadableConfiguration {
pub fn new(builder: Builder, configuration: Configuration) -> Self {
let inner = Arc::new(Inner {
builder,
config: ArcSwap::from_pointee(configuration),
registration: ArcSwap::from_pointee(Registration::none()),
token: ArcSwap::from_pointee(SharedChangeToken::default()),
});
let registration = inner
.config
.load()
.change_token()
.register(Box::new(on_changed), Some(inner.clone()));
inner.registration.store(registration.into());
Self(inner)
}
#[inline]
pub fn current(&self) -> Arc<Configuration> {
self.0.config.load_full()
}
}
#[cfg(feature = "binder")]
impl ReloadableConfiguration {
#[inline]
pub fn reify<T: DeserializeOwned>(&self) -> crate::Result<T> {
self.0.config.load().reify()
}
#[inline]
pub fn bind<T: DeserializeOwned>(&self, instance: &mut T) -> crate::Result {
self.0.config.load().bind(instance)
}
#[inline]
pub fn bind_at<T: DeserializeOwned>(&self, key: impl AsRef<str>, instance: &mut T) -> crate::Result {
self.0.config.load().bind_at(key, instance)
}
#[inline]
pub fn get_value<T: FromStr>(&self, key: impl AsRef<str>) -> Result<Option<T>, T::Err> {
self.0.config.load().get_value(key)
}
#[inline]
pub fn get_value_or_default<T: FromStr + Default>(&self, key: impl AsRef<str>) -> Result<T, T::Err> {
self.0.config.load().get_value_or_default(key)
}
}
impl Clone for ReloadableConfiguration {
#[inline]
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl Reloadable for ReloadableConfiguration {
#[inline]
fn can_reload(&self) -> bool {
true
}
#[inline]
fn reload_token(&self) -> impl ChangeToken + 'static {
(**self.0.token.load()).clone()
}
}
impl Debug for ReloadableConfiguration {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&*self.current(), f)
}
}
impl Display for ReloadableConfiguration {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&*self.current(), f)
}
}
impl From<ReloadableConfiguration> for Arc<Configuration> {
#[inline]
fn from(rc: ReloadableConfiguration) -> Self {
rc.current()
}
}
impl From<&ReloadableConfiguration> for Arc<Configuration> {
#[inline]
fn from(rc: &ReloadableConfiguration) -> Self {
rc.current()
}
}
impl TryFrom<Builder> for ReloadableConfiguration {
type Error = crate::Error;
fn try_from(builder: Builder) -> crate::Result<Self> {
let config = builder.build()?;
Ok(Self::new(builder, config))
}
}