murr 0.2.0-rc3

Columnar in-memory cache for AI/ML inference workloads
Documentation
use crate::{
    conf::{ServerConfig, StorageConfig},
    core::{
        CliArgs,
        MurrError::{self, ConfigParsingError},
    },
};
use config::Config as CConfig;
use serde::{Deserialize, Serialize};

#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Config {
    #[serde(default)]
    pub server: ServerConfig,
    #[serde(default)]
    pub storage: StorageConfig,
}

impl Config {
    pub fn from_args(args: &CliArgs) -> Result<Config, MurrError> {
        let mut builder = CConfig::builder();

        if let Some(config_path) = &args.config {
            builder = builder.add_source(config::File::with_name(config_path));
        }

        builder = builder.add_source(
            config::Environment::with_prefix("MURR")
                .separator("_")
                .try_parsing(true),
        );

        builder
            .build()
            .map_err(|e| ConfigParsingError(e.to_string()))?
            .try_deserialize::<Config>()
            .map_err(|e| ConfigParsingError(e.to_string()))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_config_default() {
        let config = Config::default();
        assert_eq!(config.server.http.host, "0.0.0.0");
        assert_eq!(config.server.http.port, 8080);
        assert_eq!(config.server.grpc.host, "0.0.0.0");
        assert_eq!(config.server.grpc.port, 8081);
    }

    #[test]
    fn test_config_from_args_no_file() {
        let args = CliArgs { config: None };
        let config = Config::from_args(&args).unwrap();
        assert_eq!(config.server.http.port, 8080);
        assert_eq!(config.server.grpc.port, 8081);
    }

    #[test]
    fn test_config_unknown_field_rejected() {
        let args = CliArgs {
            config: Some("nonexistent.yaml".to_string()),
        };
        let result = Config::from_args(&args);
        assert!(result.is_err());
    }
}