pulseengine_mcp_cli/
config.rs

1//! Configuration management and utilities
2
3use crate::CliError;
4use pulseengine_mcp_protocol::{Implementation, ProtocolVersion, ServerCapabilities, ServerInfo};
5use serde::{Deserialize, Serialize};
6use std::env;
7
8/// Default logging configuration
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct DefaultLoggingConfig {
11    pub level: String,
12    pub format: LogFormat,
13    pub output: LogOutput,
14    pub structured: bool,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub enum LogFormat {
19    #[serde(rename = "json")]
20    Json,
21    #[serde(rename = "pretty")]
22    Pretty,
23    #[serde(rename = "compact")]
24    Compact,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub enum LogOutput {
29    #[serde(rename = "stdout")]
30    Stdout,
31    #[serde(rename = "stderr")]
32    Stderr,
33    #[serde(rename = "file")]
34    File(String),
35}
36
37impl Default for DefaultLoggingConfig {
38    fn default() -> Self {
39        Self {
40            level: "info".to_string(),
41            format: LogFormat::Pretty,
42            output: LogOutput::Stdout,
43            structured: true,
44        }
45    }
46}
47
48impl DefaultLoggingConfig {
49    pub fn initialize(&self) -> Result<(), CliError> {
50        // Initialize tracing subscriber based on configuration
51        use tracing_subscriber::{EnvFilter, fmt, prelude::*};
52
53        let level = env::var("RUST_LOG").unwrap_or_else(|_| self.level.clone());
54        let filter = EnvFilter::try_from_default_env()
55            .or_else(|_| EnvFilter::try_new(&level))
56            .map_err(|e| CliError::logging(format!("Invalid log level: {e}")))?;
57
58        match self.format {
59            LogFormat::Json => {
60                tracing_subscriber::registry()
61                    .with(filter)
62                    .with(fmt::layer().json())
63                    .init();
64            }
65            LogFormat::Pretty => {
66                tracing_subscriber::registry()
67                    .with(filter)
68                    .with(fmt::layer().pretty())
69                    .init();
70            }
71            LogFormat::Compact => {
72                tracing_subscriber::registry()
73                    .with(filter)
74                    .with(fmt::layer().compact())
75                    .init();
76            }
77        }
78
79        Ok(())
80    }
81}
82
83/// Utility to create default server info from Cargo.toml
84pub fn create_server_info(name: Option<String>, version: Option<String>) -> ServerInfo {
85    ServerInfo {
86        protocol_version: ProtocolVersion::default(),
87        capabilities: ServerCapabilities::default(),
88        server_info: Implementation {
89            name: name.unwrap_or_else(|| env!("CARGO_PKG_NAME").to_string()),
90            version: version.unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string()),
91        },
92        instructions: None,
93    }
94}
95
96/// Environment variable utilities
97pub mod env_utils {
98    use std::env;
99    use std::str::FromStr;
100
101    /// Get environment variable with default value
102    pub fn get_env_or_default<T>(key: &str, default: T) -> T
103    where
104        T: FromStr + Clone,
105    {
106        env::var(key)
107            .ok()
108            .and_then(|v| v.parse().ok())
109            .unwrap_or(default)
110    }
111
112    /// Get required environment variable
113    pub fn get_required_env<T>(key: &str) -> Result<T, crate::CliError>
114    where
115        T: FromStr,
116        T::Err: std::fmt::Display,
117    {
118        env::var(key)
119            .map_err(|_| {
120                crate::CliError::configuration(format!(
121                    "Missing required environment variable: {key}"
122                ))
123            })?
124            .parse()
125            .map_err(|e| crate::CliError::configuration(format!("Invalid value for {key}: {e}")))
126    }
127}