1use std::path::PathBuf;
8
9use serde::{Deserialize, Serialize};
10
11use crate::error::{M2MError, Result};
12
13#[derive(Debug, Clone, Default, Serialize, Deserialize)]
15pub struct Config {
16 #[serde(default)]
18 pub compression: CompressionConfig,
19
20 #[serde(default)]
22 pub models: ModelConfig,
23}
24
25impl Config {
26 pub fn from_file(path: impl Into<PathBuf>) -> Result<Self> {
28 let path = path.into();
29 let content = std::fs::read_to_string(&path)
30 .map_err(|e| M2MError::Config(format!("Failed to read config file: {e}")))?;
31
32 toml::from_str(&content)
33 .map_err(|e| M2MError::Config(format!("Failed to parse config: {e}")))
34 }
35
36 pub fn from_env() -> Self {
38 let mut config = Self::default();
39
40 if let Ok(val) = std::env::var("M2M_COMPRESS_MIN_TOKENS") {
42 if let Ok(val) = val.parse() {
43 config.compression.min_tokens = val;
44 }
45 }
46
47 config
48 }
49
50 pub fn merge(self, other: Self) -> Self {
52 Self {
53 compression: other.compression,
54 models: other.models,
55 }
56 }
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct CompressionConfig {
62 pub enabled: bool,
64
65 pub min_tokens: usize,
67
68 pub full_compression_threshold: usize,
70
71 pub abbreviate_keys: bool,
73
74 pub abbreviate_roles: bool,
76
77 pub abbreviate_models: bool,
79
80 pub remove_defaults: bool,
82}
83
84impl Default for CompressionConfig {
85 fn default() -> Self {
86 Self {
87 enabled: true,
88 min_tokens: 25,
89 full_compression_threshold: 50,
90 abbreviate_keys: true,
91 abbreviate_roles: true,
92 abbreviate_models: true,
93 remove_defaults: true,
94 }
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct ModelConfig {
101 pub fetch_openrouter: bool,
103
104 pub cache_dir: Option<PathBuf>,
106
107 pub cache_ttl_secs: u64,
109}
110
111impl Default for ModelConfig {
112 fn default() -> Self {
113 Self {
114 fetch_openrouter: false,
115 cache_dir: dirs::cache_dir().map(|p| p.join("m2m")),
116 cache_ttl_secs: 3600, }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_default_config() {
127 let config = Config::default();
128 assert!(config.compression.abbreviate_keys);
129 }
130
131 #[test]
132 fn test_config_from_toml() {
133 let toml = r#"
134 [compression]
135 enabled = true
136 min_tokens = 50
137 full_compression_threshold = 50
138 abbreviate_keys = true
139 abbreviate_roles = true
140 abbreviate_models = true
141 remove_defaults = true
142 "#;
143
144 let config: Config = toml::from_str(toml).unwrap();
145 assert_eq!(config.compression.min_tokens, 50);
146 assert!(config.compression.enabled);
147 }
148}