grafbabe 0.1.1

Simple openmetrics stats collector
use super::parse_ini::ParseError;
use super::version;
use log::Level;
use std::fmt;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::{Path, PathBuf};
use std::time::Duration;

pub const DEFAULT_PORT: u16 = 4242;
const DEFAULT_LISTEN_ADDR: SocketAddr = if cfg!(debug_assertions) {
    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), DEFAULT_PORT)
} else {
    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), DEFAULT_PORT)
};

#[derive(Debug)]
pub struct Config {
    pub command: Command,
    pub info_command: Option<InfoCommand>,
    pub listen_addrs: Vec<SocketAddr>,
    pub frontend_dir: Option<String>,
    pub prometheus_addr: String,
    pub poll_rate_mins: u64,
    pub state_location: PathBuf,
    pub database_name: String,
    pub log_level: Level,
    pub log_format: LogFormat,
    pub log_target: LogTarget,
}

impl Default for Config {
    fn default() -> Self {
        Config {
            command: Command::Serve,
            info_command: None,
            listen_addrs: vec![DEFAULT_LISTEN_ADDR],
            frontend_dir: if cfg!(debug_assertions) {
                Some("frontend".to_string())
            } else {
                None
            },
            prometheus_addr: "http://localhost/metrics".to_string(),
            poll_rate_mins: 1,
            state_location: PathBuf::from("."),
            database_name: version::NAME.into(),
            log_level: Level::Info,
            log_format: LogFormat::Plain,
            log_target: LogTarget::Stderr,
        }
    }
}

impl Config {
    pub fn database_name(&self) -> PathBuf {
        Path::new(&self.database_name).with_added_extension("db3")
    }

    pub fn database_path(&self) -> PathBuf {
        self.state_location.join(self.database_name())
    }

    pub fn poll_rate_duration(&self) -> Duration {
        Duration::from_mins(self.poll_rate_mins)
    }
}

#[derive(Clone, Debug, Default)]
pub enum Command {
    #[default]
    Serve,
    #[cfg(feature = "mock_data")]
    ServeMockData(String),
    #[cfg(feature = "mock_data")]
    Seed(String),
}

#[derive(Clone, Copy, Debug)]
pub enum InfoCommand {
    Usage,
    Version,
    VersionMore,
}

#[derive(Copy, Clone, Debug, Default)]
pub enum LogFormat {
    #[default]
    Plain,
    Pretty,
}

#[derive(Copy, Clone, Debug, Default)]
pub enum LogTarget {
    None,
    Stdout,
    #[default]
    Stderr,
}

#[derive(Debug)]
pub enum ConfigError {
    MissingArgument(String),
    #[cfg(feature = "mock_data")]
    MissingCommandArgument(String),
    UnrecognizedArgument(String),
    UnrecognizedCommand(String),
    ParseError(PathBuf, ParseError),
}

impl fmt::Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ConfigError::MissingArgument(s) => {
                write!(f, "the flag {s} is missing its following argument")
            }
            #[cfg(feature = "mock_data")]
            ConfigError::MissingCommandArgument(s) => {
                write!(f, "the command {s} is missing its following argument")
            }
            ConfigError::UnrecognizedArgument(s) => write!(f, "unrecognized argument {s}"),
            ConfigError::UnrecognizedCommand(s) => write!(f, "unrecognized command {s}"),
            ConfigError::ParseError(p, e) => {
                write!(f, "error parsing config file {}: {e}", p.to_string_lossy())
            }
        }
    }
}