idemio 0.1.1

Core framework for idemio products
Documentation
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fs::File;
use std::path::Path;
use thiserror::Error;

#[derive(Debug)]
pub struct HandlerConfig<C>
where
    C: Default + DeserializeOwned,
{
    id: String,
    enabled: bool,
    timeout: Option<u64>,
    retry_count: Option<u32>,
    retry_delay: Option<u64>,
    config: Config<C>,
}

impl<C> Default for HandlerConfig<C>
where
    C: Default + DeserializeOwned,
{
    fn default() -> Self {
        Self {
            id: "unknown".to_string(),
            enabled: true,
            timeout: Some(30000),
            retry_count: None,
            retry_delay: None,
            config: Config::default(),
        }
    }
}

impl<C> HandlerConfig<C>
where
    C: Default + DeserializeOwned,
{
    pub fn new(id: String, config: Config<C>) -> Self {
        Self {
            id,
            config,
            ..Default::default()
        }
    }

    pub fn id(&self) -> &String {
        &self.id
    }

    pub fn enabled(&self) -> bool {
        self.enabled
    }

    pub fn timeout(&self) -> Option<u64> {
        self.timeout
    }

    pub fn retry_count(&self) -> Option<u32> {
        self.retry_count
    }

    pub fn retry_delay(&self) -> Option<u64> {
        self.retry_delay
    }

    pub fn config(&self) -> &Config<C> {
        &self.config
    }

    pub fn set_config(&mut self, config: Config<C>) {
        self.config = config;
    }

    pub fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
    }

    pub fn set_timeout(&mut self, timeout: Option<u64>) {
        self.timeout = timeout;
    }

    pub fn set_retry_count(&mut self, retry_count: Option<u32>) {
        self.retry_count = retry_count;
    }

    pub fn set_retry_delay(&mut self, retry_delay: Option<u64>) {
        self.retry_delay = retry_delay;
    }

    pub fn set_id(&mut self, id: String) {
        self.id = id;
    }

    pub fn builder() -> HandlerConfigBuilder<C> {
        HandlerConfigBuilder::new()
    }
}

pub struct HandlerConfigBuilder<C>
where
    C: Default + DeserializeOwned,
{
    config: HandlerConfig<C>,
}

impl<C> HandlerConfigBuilder<C>
where
    C: Default + DeserializeOwned,
{
    pub fn new() -> Self {
        Self {
            config: HandlerConfig::<C>::default(),
        }
    }

    pub fn id(&mut self, id: impl Into<String>) -> &mut Self {
        self.config.id = id.into();
        self
    }

    pub fn enabled(&mut self, enabled: bool) -> &mut Self {
        self.config.enabled = enabled;
        self
    }

    pub fn timeout(&mut self, timeout: Option<u64>) -> &mut Self {
        self.config.timeout = timeout;
        self
    }

    pub fn retry_count(&mut self, retry_count: Option<u32>) -> &mut Self {
        self.config.retry_count = retry_count;
        self
    }

    pub fn retry_delay(&mut self, retry_delay: Option<u64>) -> &mut Self {
        self.config.retry_delay = retry_delay;
        self
    }

    pub fn handler_config(&mut self, config: Config<C>) -> &mut Self {
        self.config.config = config;
        self
    }

    pub fn build(self) -> HandlerConfig<C> {
        self.config
    }
}

#[derive(Debug, Default)]
pub struct Config<C> {
    config: C,
}

impl<C> Config<C>
where
    C: Default + DeserializeOwned,
{
    pub fn new(provider: impl ConfigProvider<C>) -> Result<Self, ()> {
        provider
            .load()
            .map(|config| Config { config })
            .map_err(|_| ())
    }

    pub fn get(&self) -> &C {
        &self.config
    }

    pub fn get_mut(&mut self) -> &mut C {
        &mut self.config
    }
}

pub trait ConfigProvider<C>
where
    C: Default + DeserializeOwned,
{
    fn load(&self) -> Result<C, ConfigProviderError>;
}

pub struct DefaultConfigProvider;

impl<C> ConfigProvider<C> for DefaultConfigProvider
where
    C: Default + DeserializeOwned,
{
    fn load(&self) -> Result<C, ConfigProviderError> {
        Ok(C::default())
    }
}

pub struct FileConfigProvider {
    pub base_path: String,
    pub config_name: String,
}

impl<C> ConfigProvider<C> for FileConfigProvider
where
    C: Default + DeserializeOwned,
{
    fn load(&self) -> Result<C, ConfigProviderError> {
        let config_path = Path::new(&self.base_path).join(&self.config_name);
        let file = File::open(config_path).map_err(|e| {
            let msg = format!("Could not open config file: {}", e);
            ConfigProviderError::load_error(msg)
        })?;
        serde_json::from_reader(file).map_err(|e| {
            let msg = format!("Could not load config file from reader: {}", e);
            ConfigProviderError::load_error(msg)
        })
    }
}

pub struct ProgrammaticConfigProvider<C> {
    pub config: C,
}

impl<C> ConfigProvider<C> for ProgrammaticConfigProvider<C>
where
    C: Default + DeserializeOwned + Clone + Serialize,
{
    fn load(&self) -> Result<C, ConfigProviderError> {
        Ok(self.config.clone())
    }
}

pub enum ProviderType {
    File,
    Default,
}

#[derive(Error, Debug)]
pub enum ConfigProviderError {
    #[error("Could not load config file. {message}")]
    Load { message: String },
}

impl ConfigProviderError {
    #[inline]
    pub(crate) fn load_error(msg: impl Into<String>) -> Self {
        Self::Load {
            message: msg.into(),
        }
    }
}