vkteams_bot/config/
types.rs

1use once_cell::sync::Lazy;
2use serde::{self, Deserialize, Serialize};
3use std::borrow::Cow;
4
5pub static APP_FOLDER: &str = "VKTEAMS_BOT_CONFIG";
6pub static CONFIG: Lazy<Config> = Lazy::new(Config::new);
7/// Configuration file
8#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
9#[serde(rename_all = "snake_case")]
10pub struct Config {
11    #[cfg(feature = "otlp")]
12    #[serde(default)]
13    pub otlp: OtlpConfig,
14    #[cfg(feature = "ratelimit")]
15    #[serde(default)]
16    pub rate_limit: RateLimit,
17    #[serde(default)]
18    pub network: NetworkConfig,
19    #[cfg(feature = "longpoll")]
20    #[serde(default)]
21    pub listener: EventListenerConfig,
22    #[cfg(feature = "storage")]
23    #[serde(default)]
24    pub storage: crate::storage::StorageConfig,
25}
26
27/// Otlp variables
28#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
29#[serde(rename_all = "snake_case")]
30#[repr(C)]
31pub struct OtlpConfig {
32    #[serde(default = "default_instance_id")]
33    pub instance_id: Cow<'static, str>,
34    #[serde(default = "default_deployment_environment_name")]
35    pub deployment_environment_name: Cow<'static, str>,
36    #[serde(default = "default_exporter_endpoint")]
37    pub exporter_endpoint: Option<Cow<'static, str>>,
38    #[serde(default = "default_exporter_timeout")]
39    pub exporter_timeout: u64,
40    #[serde(default = "default_exporter_metric_interval")]
41    pub exporter_metric_interval: u64,
42    #[serde(default = "default_ratio")]
43    pub ratio: f64,
44    #[serde(default = "default_otlp_filter_default")]
45    pub otel_filter_default: Cow<'static, str>,
46    #[serde(default = "default_fmt_filter_default")]
47    pub fmt_filter_default: Cow<'static, str>,
48    #[serde(default = "default_fmt_ansi")]
49    pub fmt_ansi: bool,
50    #[serde(default = "default_fmt_filter_self_directive")]
51    pub fmt_filter_self_directive: Cow<'static, str>,
52    #[serde(default = "default_otel")]
53    pub otel: Vec<OtelDirective>,
54    #[serde(default = "default_fmt")]
55    pub fmt: Vec<FmtDirective>,
56    #[serde(default)]
57    pub log_format: LogFormat,
58}
59
60impl Default for OtlpConfig {
61    fn default() -> Self {
62        Self {
63            instance_id: default_instance_id(),
64            deployment_environment_name: default_deployment_environment_name(),
65            exporter_endpoint: default_exporter_endpoint(),
66            exporter_timeout: default_exporter_timeout(),
67            exporter_metric_interval: default_exporter_metric_interval(),
68            ratio: default_ratio(),
69            otel_filter_default: default_otlp_filter_default(),
70            fmt_filter_default: default_fmt_filter_default(),
71            fmt_ansi: default_fmt_ansi(),
72            fmt_filter_self_directive: default_fmt_filter_self_directive(),
73            otel: default_otel(),
74            fmt: default_fmt(),
75            log_format: LogFormat::default(),
76        }
77    }
78}
79
80fn default_instance_id() -> Cow<'static, str> {
81    Cow::Borrowed("bot")
82}
83fn default_deployment_environment_name() -> Cow<'static, str> {
84    Cow::Borrowed("dev")
85}
86fn default_exporter_endpoint() -> Option<Cow<'static, str>> {
87    None
88}
89fn default_exporter_timeout() -> u64 {
90    0
91}
92fn default_exporter_metric_interval() -> u64 {
93    0
94}
95fn default_ratio() -> f64 {
96    1.0
97}
98fn default_otlp_filter_default() -> Cow<'static, str> {
99    Cow::Borrowed("debug")
100}
101fn default_fmt_filter_default() -> Cow<'static, str> {
102    Cow::Borrowed("debug")
103}
104fn default_fmt_ansi() -> bool {
105    true
106}
107fn default_fmt_filter_self_directive() -> Cow<'static, str> {
108    Cow::Borrowed("debug")
109}
110fn default_otel() -> Vec<OtelDirective> {
111    vec![OtelDirective {
112        otel_filter_directive: Cow::Borrowed("h2=off"),
113    }]
114}
115fn default_fmt() -> Vec<FmtDirective> {
116    vec![
117        FmtDirective {
118            fmt_filter_directive: Cow::Borrowed("axum=trace"),
119        },
120        FmtDirective {
121            fmt_filter_directive: Cow::Borrowed("tower_http=debug"),
122        },
123        FmtDirective {
124            fmt_filter_directive: Cow::Borrowed("opentelemetry=info"),
125        },
126    ]
127}
128
129#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
130#[serde(rename_all = "snake_case")]
131#[repr(C)]
132pub struct OtelDirective {
133    pub otel_filter_directive: Cow<'static, str>,
134}
135
136#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
137#[serde(rename_all = "snake_case")]
138#[repr(C)]
139pub struct FmtDirective {
140    pub fmt_filter_directive: Cow<'static, str>,
141}
142/// Rate limit configuration
143#[cfg(feature = "ratelimit")]
144#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
145#[serde(rename_all = "snake_case")]
146pub struct RateLimit {
147    #[serde(default = "default_limit")]
148    pub limit: usize,
149    #[serde(default = "default_duration")]
150    pub duration: u64,
151    #[serde(default = "default_retry_delay")]
152    pub retry_delay: u64,
153    #[serde(default = "default_retry_attempts")]
154    pub retry_attempts: u16,
155    #[serde(default = "default_init_bucket")]
156    pub init_bucket: usize,
157    #[serde(default = "default_cleanup_interval")]
158    pub cleanup_interval: u64,
159    #[serde(default = "default_bucket_lifetime")]
160    pub bucket_lifetime: u64,
161}
162
163#[cfg(feature = "ratelimit")]
164impl Default for RateLimit {
165    fn default() -> Self {
166        Self {
167            limit: default_limit(),
168            duration: default_duration(),
169            retry_delay: default_retry_delay(),
170            retry_attempts: default_retry_attempts(),
171            init_bucket: default_init_bucket(),
172            cleanup_interval: default_cleanup_interval(),
173            bucket_lifetime: default_bucket_lifetime(),
174        }
175    }
176}
177#[cfg(feature = "ratelimit")]
178fn default_limit() -> usize {
179    100
180}
181#[cfg(feature = "ratelimit")]
182fn default_duration() -> u64 {
183    60
184}
185#[cfg(feature = "ratelimit")]
186fn default_retry_delay() -> u64 {
187    1000
188}
189#[cfg(feature = "ratelimit")]
190fn default_retry_attempts() -> u16 {
191    3
192}
193#[cfg(feature = "ratelimit")]
194fn default_init_bucket() -> usize {
195    1
196}
197#[cfg(feature = "ratelimit")]
198fn default_cleanup_interval() -> u64 {
199    600
200}
201#[cfg(feature = "ratelimit")]
202fn default_bucket_lifetime() -> u64 {
203    3600
204}
205
206/// Configuration for event listener
207#[cfg(feature = "longpoll")]
208#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
209#[serde(rename_all = "snake_case")]
210pub struct EventListenerConfig {
211    /// Maximum number of events to process in a single batch
212    #[serde(default = "default_max_events_per_batch")]
213    pub max_events_per_batch: usize,
214    /// Backoff time in milliseconds when no events received
215    #[serde(default = "default_empty_backoff_ms")]
216    pub empty_backoff_ms: u64,
217    /// Maximum backoff time in milliseconds
218    #[serde(default = "default_max_backoff_ms")]
219    pub max_backoff_ms: u64,
220    /// Whether to use exponential backoff when no events received
221    #[serde(default = "default_use_exponential_backoff")]
222    pub use_exponential_backoff: bool,
223    /// Maximum memory usage for event processing in bytes (0 means no limit)
224    #[serde(default = "default_max_memory_usage")]
225    pub max_memory_usage: usize,
226}
227
228#[cfg(feature = "longpoll")]
229impl Default for EventListenerConfig {
230    fn default() -> Self {
231        Self {
232            max_events_per_batch: default_max_events_per_batch(),
233            empty_backoff_ms: default_empty_backoff_ms(),
234            max_backoff_ms: default_max_backoff_ms(),
235            use_exponential_backoff: default_use_exponential_backoff(),
236            max_memory_usage: default_max_memory_usage(),
237        }
238    }
239}
240
241#[cfg(feature = "longpoll")]
242fn default_max_events_per_batch() -> usize {
243    50
244}
245#[cfg(feature = "longpoll")]
246fn default_empty_backoff_ms() -> u64 {
247    500
248}
249fn default_max_backoff_ms() -> u64 {
250    5000
251}
252#[cfg(feature = "longpoll")]
253fn default_use_exponential_backoff() -> bool {
254    true
255}
256#[cfg(feature = "longpoll")]
257fn default_max_memory_usage() -> usize {
258    0
259}
260
261/// Network configuration
262#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
263#[serde(rename_all = "snake_case")]
264pub struct NetworkConfig {
265    /// Number of retry attempts for failed requests
266    #[serde(default = "default_retries")]
267    pub retries: usize,
268    /// Maximum backoff time in milliseconds
269    #[serde(default = "default_max_backoff_ms")]
270    pub max_backoff_ms: u64,
271    /// Request timeout in seconds
272    #[serde(default = "default_request_timeout_secs")]
273    pub request_timeout_secs: u64,
274    /// Connection timeout in seconds
275    #[serde(default = "default_connect_timeout_secs")]
276    pub connect_timeout_secs: u64,
277    /// Pool idle timeout in seconds
278    #[serde(default = "default_pool_idle_timeout_secs")]
279    pub pool_idle_timeout_secs: u64,
280    /// Maximum number of idle connections per host
281    #[serde(default = "default_max_idle_connections")]
282    pub max_idle_connections: usize,
283}
284
285impl Default for NetworkConfig {
286    fn default() -> Self {
287        Self {
288            retries: default_retries(),
289            max_backoff_ms: default_max_backoff_ms(),
290            request_timeout_secs: default_request_timeout_secs(),
291            connect_timeout_secs: default_connect_timeout_secs(),
292            pool_idle_timeout_secs: default_pool_idle_timeout_secs(),
293            max_idle_connections: default_max_idle_connections(),
294        }
295    }
296}
297
298fn default_retries() -> usize {
299    3
300}
301fn default_request_timeout_secs() -> u64 {
302    30
303}
304fn default_connect_timeout_secs() -> u64 {
305    10
306}
307fn default_pool_idle_timeout_secs() -> u64 {
308    90
309}
310fn default_max_idle_connections() -> usize {
311    10
312}
313
314#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
315#[serde(rename_all = "snake_case")]
316pub enum LogFormat {
317    #[default]
318    Pretty,
319    Json,
320    Full,
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326    use toml;
327
328    #[test]
329    fn test_config_defaults() {
330        let config = Config::default();
331        #[cfg(feature = "otlp")]
332        {
333            // Проверяем, что otlp присутствует и instance_id по умолчанию "bot"
334            assert_eq!(config.otlp.instance_id, "bot");
335        }
336        #[cfg(feature = "ratelimit")]
337        {
338            // Проверяем значения по умолчанию для rate_limit
339            assert_eq!(config.rate_limit.limit, 100);
340        }
341        #[cfg(feature = "storage")]
342        {
343            // Проверяем storage config
344            assert_eq!(config.storage.database.max_connections, 20);
345            assert!(config.storage.database.auto_migrate);
346        }
347        // Проверяем network config
348        assert_eq!(config.network.retries, 3);
349        assert_eq!(config.network.request_timeout_secs, 30);
350    }
351
352    #[test]
353    fn test_serialize_deserialize_config() {
354        let mut config = Config::default();
355        #[cfg(feature = "otlp")]
356        {
357            config.otlp.instance_id = "test_id".into();
358        }
359        #[cfg(feature = "storage")]
360        {
361            config.storage.database.max_connections = 42;
362        }
363        config.network.retries = 7;
364        let toml_str = toml::to_string(&config).unwrap();
365        let deser: Config = toml::from_str(&toml_str).unwrap();
366        #[cfg(feature = "otlp")]
367        {
368            assert_eq!(deser.otlp.instance_id, "test_id");
369        }
370        #[cfg(feature = "storage")]
371        {
372            assert_eq!(deser.storage.database.max_connections, 42);
373        }
374        assert_eq!(deser.network.retries, 7);
375    }
376
377    #[cfg(feature = "ratelimit")]
378    #[test]
379    fn test_default_limit() {
380        assert_eq!(default_limit(), 100);
381    }
382
383    #[cfg(feature = "ratelimit")]
384    #[test]
385    fn test_default_duration() {
386        assert_eq!(default_duration(), 60);
387    }
388
389    #[cfg(feature = "ratelimit")]
390    #[test]
391    fn test_default_retry_delay() {
392        assert_eq!(default_retry_delay(), 1000);
393    }
394}