1use crate::RoutingMode;
6use crate::types::{CacheCapacity, ConfigPath, Port, ThreadCount};
7use clap::Parser;
8
9#[derive(Parser, Debug, Clone)]
13pub struct CommonArgs {
14 #[arg(short, long, env)]
16 pub port: Option<Port>,
17
18 #[arg(long, env)]
20 pub host: Option<String>,
21
22 #[arg(
28 short = 'm',
29 long = "routing-mode",
30 value_enum,
31 default_value = "hybrid",
32 env
33 )]
34 pub routing_mode: RoutingMode,
35
36 #[arg(short, long, default_value = "config.toml", env)]
38 pub config: ConfigPath,
39
40 #[arg(short, long, env)]
42 pub threads: Option<ThreadCount>,
43}
44
45impl CommonArgs {
46 const DEFAULT_PORT: u16 = 8119;
48
49 const DEFAULT_HOST: &'static str = "0.0.0.0";
51
52 #[must_use]
60 pub fn listen_addr(&self, config_port: Option<Port>) -> String {
61 let port = self
62 .effective_port(config_port)
63 .map_or(Self::DEFAULT_PORT, |p| p.get());
64
65 format!("{}:{}", self.effective_host(), port)
66 }
67
68 #[must_use]
70 pub const fn effective_port(&self, config_port: Option<Port>) -> Option<Port> {
71 match self.port {
72 Some(p) => Some(p),
73 None => config_port,
74 }
75 }
76
77 #[must_use]
79 pub fn effective_host(&self) -> &str {
80 self.host.as_deref().unwrap_or(Self::DEFAULT_HOST)
81 }
82}
83
84#[derive(Parser, Debug, Clone)]
86pub struct CacheArgs {
87 #[arg(long, default_value = "10000", env)]
89 pub cache_capacity: CacheCapacity,
90
91 #[arg(long, default_value = "3600", env)]
93 pub cache_ttl: u64,
94}
95
96impl CacheArgs {
97 #[must_use]
99 pub const fn ttl(&self) -> std::time::Duration {
100 std::time::Duration::from_secs(self.cache_ttl)
101 }
102
103 #[must_use]
105 pub fn capacity(&self) -> usize {
106 self.cache_capacity.get()
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_common_args_defaults() {
116 let args = CommonArgs {
117 port: None,
118 host: None,
119 routing_mode: RoutingMode::Hybrid,
120 config: ConfigPath::new("config.toml").unwrap(),
121 threads: None,
122 };
123
124 assert_eq!(args.listen_addr(None), "0.0.0.0:8119");
125 assert_eq!(args.effective_host(), "0.0.0.0");
126 assert!(args.effective_port(None).is_none());
127 }
128
129 #[test]
130 fn test_common_args_with_port() {
131 let args = CommonArgs {
132 port: Some(Port::new(9119).unwrap()),
133 host: None,
134 routing_mode: RoutingMode::Hybrid,
135 config: ConfigPath::new("config.toml").unwrap(),
136 threads: None,
137 };
138
139 assert_eq!(args.listen_addr(None), "0.0.0.0:9119");
140 assert_eq!(args.effective_port(None), Some(Port::new(9119).unwrap()));
141 }
142
143 #[test]
144 fn test_common_args_with_host() {
145 let args = CommonArgs {
146 port: None,
147 host: Some("127.0.0.1".to_string()),
148 routing_mode: RoutingMode::Hybrid,
149 config: ConfigPath::new("config.toml").unwrap(),
150 threads: None,
151 };
152
153 assert_eq!(args.listen_addr(None), "127.0.0.1:8119");
154 assert_eq!(args.effective_host(), "127.0.0.1");
155 }
156
157 #[test]
158 fn test_common_args_port_override() {
159 let args = CommonArgs {
160 port: Some(Port::new(9119).unwrap()),
161 host: None,
162 routing_mode: RoutingMode::Hybrid,
163 config: ConfigPath::new("config.toml").unwrap(),
164 threads: None,
165 };
166
167 let config_port = Some(Port::new(7119).unwrap());
168
169 assert_eq!(args.listen_addr(config_port), "0.0.0.0:9119");
171 assert_eq!(
172 args.effective_port(config_port),
173 Some(Port::new(9119).unwrap())
174 );
175 }
176
177 #[test]
178 fn test_common_args_config_port_fallback() {
179 let args = CommonArgs {
180 port: None,
181 host: None,
182 routing_mode: RoutingMode::Hybrid,
183 config: ConfigPath::new("config.toml").unwrap(),
184 threads: None,
185 };
186
187 let config_port = Some(Port::new(7119).unwrap());
188
189 assert_eq!(args.listen_addr(config_port), "0.0.0.0:7119");
191 assert_eq!(
192 args.effective_port(config_port),
193 Some(Port::new(7119).unwrap())
194 );
195 }
196
197 #[test]
198 fn test_common_args_custom_host_and_port() {
199 let args = CommonArgs {
200 port: Some(Port::new(9119).unwrap()),
201 host: Some("192.168.1.1".to_string()),
202 routing_mode: RoutingMode::Hybrid,
203 config: ConfigPath::new("config.toml").unwrap(),
204 threads: None,
205 };
206
207 assert_eq!(args.listen_addr(None), "192.168.1.1:9119");
208 assert_eq!(args.effective_host(), "192.168.1.1");
209 assert_eq!(args.effective_port(None), Some(Port::new(9119).unwrap()));
210 }
211
212 #[test]
213 fn test_cache_args_defaults() {
214 let args = CacheArgs {
215 cache_capacity: CacheCapacity::new(10000).unwrap(),
216 cache_ttl: 3600,
217 };
218
219 assert_eq!(args.capacity(), 10000);
220 assert_eq!(args.cache_ttl, 3600);
221 assert_eq!(args.ttl(), std::time::Duration::from_secs(3600));
222 }
223
224 #[test]
225 fn test_cache_args_custom_values() {
226 let args = CacheArgs {
227 cache_capacity: CacheCapacity::new(50000).unwrap(),
228 cache_ttl: 7200,
229 };
230
231 assert_eq!(args.capacity(), 50000);
232 assert_eq!(args.cache_ttl, 7200);
233 assert_eq!(args.ttl(), std::time::Duration::from_secs(7200));
234 }
235
236 #[test]
237 fn test_routing_modes() {
238 let hybrid = CommonArgs {
239 routing_mode: RoutingMode::Hybrid,
240 ..default_args()
241 };
242 assert_eq!(hybrid.routing_mode, RoutingMode::Hybrid);
243
244 let stateful = CommonArgs {
245 routing_mode: RoutingMode::Stateful,
246 ..default_args()
247 };
248 assert_eq!(stateful.routing_mode, RoutingMode::Stateful);
249
250 let per_command = CommonArgs {
251 routing_mode: RoutingMode::PerCommand,
252 ..default_args()
253 };
254 assert_eq!(per_command.routing_mode, RoutingMode::PerCommand);
255 }
256
257 #[test]
258 fn test_thread_count() {
259 let default_threads = CommonArgs {
260 threads: None,
261 ..default_args()
262 };
263 assert!(default_threads.threads.is_none());
264
265 let single_thread = CommonArgs {
266 threads: Some(ThreadCount::new(1).unwrap()),
267 ..default_args()
268 };
269 assert_eq!(single_thread.threads, Some(ThreadCount::new(1).unwrap()));
270
271 let multi_thread = CommonArgs {
272 threads: Some(ThreadCount::new(4).unwrap()),
273 ..default_args()
274 };
275 assert_eq!(multi_thread.threads, Some(ThreadCount::new(4).unwrap()));
276 }
277
278 fn default_args() -> CommonArgs {
280 CommonArgs {
281 port: None,
282 host: None,
283 routing_mode: RoutingMode::Hybrid,
284 config: ConfigPath::new("config.toml").unwrap(),
285 threads: None,
286 }
287 }
288}