use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use serde::de::DeserializeOwned;
use crate::builder::Builder;
use crate::cell::ConfigCell;
use crate::error::Error;
static NEXT_INSTANCE: AtomicU64 = AtomicU64::new(1);
pub struct Dynamic<T> {
cell: Arc<ConfigCell<T>>,
builder: Builder<T>,
id: u64,
#[cfg(feature = "watch")]
watch_name: std::sync::OnceLock<&'static str>,
}
impl<T: DeserializeOwned + Send + Sync + 'static> Dynamic<T> {
#[must_use]
pub fn new(builder: Builder<T>) -> Self {
let cell = Arc::new(ConfigCell::new());
Self {
builder: builder.with_cell(Arc::clone(&cell)),
cell,
id: NEXT_INSTANCE.fetch_add(1, Ordering::Relaxed),
#[cfg(feature = "watch")]
watch_name: std::sync::OnceLock::new(),
}
}
pub fn init(&self) -> Result<(), Error> {
self.builder.init()
}
#[must_use]
pub fn current(&self) -> Option<Arc<T>> {
self.cell.load()
}
pub fn load(&self) -> Result<T, Error> {
self.builder.load()
}
pub fn reload(&self) -> Result<(), Error> {
self.builder.reload()
}
pub fn on_reload(&self, hook: impl Fn(&Arc<T>, &Arc<T>) + Send + Sync + 'static) {
self.cell.on_reload(hook);
}
pub fn on_reload_scoped(
&self,
hook: impl Fn(&Arc<T>, &Arc<T>) + Send + Sync + 'static,
) -> crate::HookGuard<T> {
ConfigCell::on_reload_scoped_shared(&self.cell, hook)
}
#[must_use]
pub fn builder(&self) -> &Builder<T> {
&self.builder
}
#[must_use]
pub fn key(&self) -> &str {
self.builder.key()
}
}
#[cfg(feature = "watch")]
#[cfg_attr(docsrs, doc(cfg(feature = "watch")))]
impl<T: DeserializeOwned + Send + Sync + 'static> Dynamic<T> {
pub fn watch(
&self,
debounce: core::time::Duration,
) -> std::io::Result<crate::watch::WatchHandle> {
self.watch_with(debounce, crate::watch::WatchMode::Native)
}
pub fn watch_with(
&self,
debounce: core::time::Duration,
mode: crate::watch::WatchMode,
) -> std::io::Result<crate::watch::WatchHandle> {
let name = self.watch_name.get_or_init(|| {
Box::leak(format!("dynamic:{}#{}", self.builder.key(), self.id).into_boxed_str())
});
self.builder.watch_as(
crate::watch::WatchKey::Instance(self.id),
name,
debounce,
mode,
)
}
}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
impl<T: DeserializeOwned + Send + Sync + 'static> Dynamic<T> {
#[must_use]
pub fn changes(&self) -> crate::Changes<T> {
crate::Changes::new_shared(Arc::clone(&self.cell))
}
pub async fn load_async(&self) -> Result<T, Error> {
self.builder.load_async().await
}
pub async fn init_async(&self) -> Result<(), Error> {
self.builder.init_async().await
}
}
impl<T> std::fmt::Debug for Dynamic<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Dynamic")
.field("id", &self.id)
.field("builder", &self.builder)
.finish_non_exhaustive()
}
}