use crate::error::ConfigError;
use figment::Figment;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::time::Duration;
pub trait ConfigProvider: Send + Sync {
fn load(&self) -> Result<Figment, ConfigError>;
fn name(&self) -> &str;
fn is_available(&self) -> bool;
fn priority(&self) -> u8;
fn metadata(&self) -> ProviderMetadata;
fn as_any(&self) -> &dyn std::any::Any;
}
#[derive(Debug, Clone)]
pub struct ProviderMetadata {
pub name: String,
pub description: String,
pub source_type: ProviderType,
pub requires_network: bool,
pub supports_watch: bool,
pub priority: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProviderType {
File,
Environment,
Remote,
Cli,
Serialized,
Default,
}
pub trait WatchableProvider: ConfigProvider {
fn start_watching(&mut self) -> Result<(), ConfigError>;
fn stop_watching(&mut self) -> Result<(), ConfigError>;
fn is_watching(&self) -> bool;
fn poll_interval(&self) -> Option<Duration>;
}
pub struct WatchableProviderWrapper {
inner: Arc<RwLock<Box<dyn WatchableProvider>>>,
name: String,
}
impl WatchableProviderWrapper {
pub fn new(provider: Box<dyn WatchableProvider>) -> Self {
let name = provider.name().to_string();
Self {
inner: Arc::new(RwLock::new(provider)),
name,
}
}
pub fn start_watching_safe(&self) -> Result<(), ConfigError> {
match self.inner.try_write() {
Ok(mut guard) => guard.start_watching(),
Err(_) => Err(ConfigError::RuntimeError(
"Failed to acquire write lock for start_watching".to_string(),
)),
}
}
pub fn stop_watching_safe(&self) -> Result<(), ConfigError> {
match self.inner.try_write() {
Ok(mut guard) => guard.stop_watching(),
Err(_) => Err(ConfigError::RuntimeError(
"Failed to acquire write lock for stop_watching".to_string(),
)),
}
}
}
impl ConfigProvider for WatchableProviderWrapper {
fn load(&self) -> Result<Figment, ConfigError> {
match self.inner.try_read() {
Ok(guard) => guard.load(),
Err(_) => Err(ConfigError::RuntimeError(
"Failed to acquire read lock for load".to_string(),
)),
}
}
fn name(&self) -> &str {
&self.name
}
fn is_available(&self) -> bool {
match self.inner.try_read() {
Ok(guard) => guard.is_available(),
Err(_) => false,
}
}
fn priority(&self) -> u8 {
match self.inner.try_read() {
Ok(guard) => guard.priority(),
Err(_) => 0,
}
}
fn metadata(&self) -> ProviderMetadata {
match self.inner.try_read() {
Ok(guard) => guard.metadata(),
Err(_) => ProviderMetadata {
name: self.name.clone(),
description: "Unknown provider".to_string(),
source_type: ProviderType::Default,
requires_network: false,
supports_watch: false,
priority: 255,
},
}
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl WatchableProvider for WatchableProviderWrapper {
fn start_watching(&mut self) -> Result<(), ConfigError> {
match self.inner.try_write() {
Ok(mut guard) => guard.start_watching(),
Err(_) => Err(ConfigError::RuntimeError(
"Failed to acquire write lock for start_watching".to_string(),
)),
}
}
fn stop_watching(&mut self) -> Result<(), ConfigError> {
match self.inner.try_write() {
Ok(mut guard) => guard.stop_watching(),
Err(_) => Err(ConfigError::RuntimeError(
"Failed to acquire write lock for stop_watching".to_string(),
)),
}
}
fn is_watching(&self) -> bool {
match self.inner.try_read() {
Ok(guard) => guard.is_watching(),
Err(_) => false,
}
}
fn poll_interval(&self) -> Option<Duration> {
match self.inner.try_read() {
Ok(guard) => guard.poll_interval(),
Err(_) => None,
}
}
}
pub struct ProviderManager {
providers: Vec<Arc<dyn ConfigProvider>>,
watch_providers: Vec<Arc<dyn WatchableProvider>>,
}
impl ProviderManager {
pub fn new() -> Self {
Self {
providers: Vec::new(),
watch_providers: Vec::new(),
}
}
pub fn add_provider<P: ConfigProvider + 'static>(&mut self, provider: P) -> &mut Self {
self.providers.push(Arc::new(provider));
self
}
pub fn add_watchable_provider<P: WatchableProvider + 'static>(
&mut self,
provider: P,
) -> &mut Self {
let wrapper = Arc::new(WatchableProviderWrapper::new(Box::new(provider)));
self.watch_providers.push(wrapper.clone());
self.providers.push(wrapper);
self
}
pub fn load_all(&self) -> Result<Figment, ConfigError> {
let mut figment = Figment::new();
let mut provider_refs: Vec<&Arc<dyn ConfigProvider>> = self.providers.iter().collect();
provider_refs.sort_by_key(|p| p.priority());
for provider_ref in provider_refs {
let is_available = provider_ref.is_available();
if is_available {
match provider_ref.load() {
Ok(provider_figment) => {
figment = figment.merge(provider_figment);
}
Err(e) => {
return Err(e);
}
}
}
}
Ok(figment)
}
pub fn start_watching(&mut self) -> Result<(), ConfigError> {
for provider in &self.watch_providers {
if let Some(wrapper) = provider.as_any().downcast_ref::<WatchableProviderWrapper>() {
wrapper.start_watching_safe()?;
}
}
Ok(())
}
pub fn stop_watching(&mut self) -> Result<(), ConfigError> {
for provider in &self.watch_providers {
if let Some(wrapper) = provider.as_any().downcast_ref::<WatchableProviderWrapper>() {
wrapper.stop_watching_safe()?;
}
}
Ok(())
}
pub fn get_provider_status(&self) -> HashMap<String, ProviderStatus> {
let mut status = HashMap::new();
for provider in &self.providers {
let metadata = provider.metadata();
status.insert(
metadata.name.clone(),
ProviderStatus {
available: provider.is_available(),
priority: provider.priority(),
metadata,
},
);
}
status
}
}
impl Default for ProviderManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ProviderStatus {
pub available: bool,
pub priority: u8,
pub metadata: ProviderMetadata,
}
pub struct ProviderBuilder {
providers: Vec<Arc<dyn ConfigProvider>>,
}
impl ProviderBuilder {
pub fn new() -> Self {
Self {
providers: Vec::new(),
}
}
pub fn build(self) -> ProviderManager {
ProviderManager {
providers: self.providers,
watch_providers: Vec::new(),
}
}
}
impl Default for ProviderBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct SerializedProvider {
figment: Figment,
name: String,
}
impl SerializedProvider {
pub fn new(figment: Figment, name: impl Into<String>) -> Self {
Self {
figment,
name: name.into(),
}
}
}
impl ConfigProvider for SerializedProvider {
fn load(&self) -> Result<Figment, ConfigError> {
Ok(self.figment.clone())
}
fn name(&self) -> &str {
&self.name
}
fn is_available(&self) -> bool {
true
}
fn priority(&self) -> u8 {
5 }
fn metadata(&self) -> ProviderMetadata {
ProviderMetadata {
name: self.name.clone(),
description: format!("Serialized data provider '{}'", self.name),
source_type: ProviderType::Serialized,
requires_network: false,
supports_watch: false,
priority: self.priority(),
}
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}