use fancy_log::{LogLevel, log};
use live::controller::{KeyPattern, Live, LiveDir, LiveError, ScanMode};
use live::holder::Store;
use live::loader::{DynLoader, FileSource, format::AnyFormat};
use once_cell::sync::OnceCell;
use std::path::Path;
use std::sync::Arc;
mod types;
pub use types::*;
pub static CONFIG: OnceCell<ConfigManager> = OnceCell::new();
#[must_use]
pub fn get() -> &'static ConfigManager {
CONFIG.get().expect("ConfigManager not initialized")
}
pub struct ListenerManager {
pub tcp: LiveDir<TcpConfig>,
pub udp: LiveDir<UdpConfig>,
}
impl ListenerManager {
pub async fn init(config_dir: &Path) -> Result<Self, LiveError> {
let tcp_store = Arc::new(Store::new());
let udp_store = Arc::new(Store::new());
let listener_path = config_dir.join("listener");
let build_loader = || {
DynLoader::builder()
.source(FileSource::new(listener_path.to_str().unwrap()))
.format(AnyFormat::Toml)
.format(AnyFormat::Yaml)
.format(AnyFormat::Json)
.build()
.map_err(|e| LiveError::Builder(e.to_owned()))
};
let tcp = LiveDir::builder()
.store(tcp_store)
.loader(build_loader()?)
.path(&listener_path)
.pattern(KeyPattern::Bracketed)
.scan_mode(ScanMode::Subdirs {
config_file: "tcp".to_owned(),
})
.on_error(|e| {
log(
LogLevel::Warn,
&format!("✗ New TCP config is invalid. Keeping last known good version. Error: {e}"),
);
})
.build()?;
let udp = LiveDir::builder()
.store(udp_store)
.loader(build_loader()?)
.path(&listener_path)
.pattern(KeyPattern::Bracketed)
.scan_mode(ScanMode::Subdirs {
config_file: "udp".to_owned(),
})
.on_error(|e| {
log(
LogLevel::Warn,
&format!("✗ New UDP config is invalid. Keeping last known good version. Error: {e}"),
);
})
.build()?;
Ok(Self { tcp, udp })
}
#[must_use]
pub fn get_tcp(&self, port: &str) -> Option<Arc<TcpConfig>> {
self.tcp.get(port)
}
#[must_use]
pub fn get_udp(&self, port: &str) -> Option<Arc<UdpConfig>> {
self.udp.get(port)
}
pub async fn load(&self) -> Result<(), LiveError> {
self.tcp.load().await?;
self.udp.load().await?;
Ok(())
}
pub async fn start_watching(&mut self, config: live::signal::Config) -> Result<(), LiveError> {
self.tcp.start_watching(config.clone()).await?;
self.udp.start_watching(config).await?;
Ok(())
}
}
pub struct ConfigManager {
pub listeners: ListenerManager,
pub resolvers: LiveDir<ResolverConfig>,
pub applications: LiveDir<ApplicationConfig>,
pub nodes: Live<NodesConfig>,
pub lazycert: Option<Live<LazyCertConfig>>,
}
impl ConfigManager {
pub async fn init(config_dir_str: &str) -> Result<Self, LiveError> {
let config_dir = Path::new(config_dir_str);
let build_loader = || {
DynLoader::builder()
.source(FileSource::new(config_dir_str))
.format(AnyFormat::Toml)
.format(AnyFormat::Yaml)
.format(AnyFormat::Json)
.build()
.map_err(|e| LiveError::Builder(e.to_owned()))
};
let listeners = ListenerManager::init(config_dir).await?;
let resolver_path = config_dir.join("resolver");
let resolvers = LiveDir::builder()
.store(Arc::new(Store::new()))
.loader(
DynLoader::builder()
.source(FileSource::new(resolver_path.to_str().unwrap()))
.format(AnyFormat::Toml)
.format(AnyFormat::Yaml)
.format(AnyFormat::Json)
.build()
.map_err(|e| LiveError::Builder(e.to_owned()))?,
)
.path(&resolver_path)
.pattern(KeyPattern::Identity)
.scan_mode(ScanMode::Files)
.on_error(|e| {
log(
LogLevel::Warn,
&format!("✗ Resolver config reload failed. Keeping last known good version. Error: {e}"),
);
})
.build()?;
let application_path = config_dir.join("application");
let applications = LiveDir::builder()
.store(Arc::new(Store::new()))
.loader(
DynLoader::builder()
.source(FileSource::new(application_path.to_str().unwrap()))
.format(AnyFormat::Toml)
.format(AnyFormat::Yaml)
.format(AnyFormat::Json)
.build()
.map_err(|e| LiveError::Builder(e.to_owned()))?,
)
.path(&application_path)
.pattern(KeyPattern::Identity)
.scan_mode(ScanMode::Files)
.on_error(|e| {
log(
LogLevel::Warn,
&format!(
"✗ Application config reload failed. Keeping last known good version. Error: {e}"
),
);
})
.build()?;
let nodes = Live::new(Arc::new(Store::new()), build_loader()?, "nodes");
let lazycert_live = Live::new(Arc::new(Store::new()), build_loader()?, "lazycert");
Ok(Self {
listeners,
resolvers,
applications,
nodes,
lazycert: Some(lazycert_live),
})
}
}