1use std::fmt;
2use std::time::Duration;
3
4use serde::{Deserialize, Serialize};
5
6pub const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
8
9#[derive(Clone, Serialize, Deserialize)]
14#[serde(default)]
15pub struct UnifiConfig {
16 pub url: String,
18 pub api_key: String,
20 pub site: String,
22 pub skip_tls_verify: bool,
26 pub legacy: bool,
28 #[serde(with = "duration_secs")]
33 pub request_timeout: Duration,
34}
35
36impl Default for UnifiConfig {
37 fn default() -> Self {
38 Self {
39 url: String::new(),
40 api_key: String::new(),
41 site: "default".to_string(),
42 skip_tls_verify: false,
43 legacy: false,
44 request_timeout: DEFAULT_REQUEST_TIMEOUT,
45 }
46 }
47}
48
49impl fmt::Debug for UnifiConfig {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 f.debug_struct("UnifiConfig")
52 .field("url", &self.url)
53 .field(
54 "api_key",
55 &format_args!("<redacted, {} bytes>", self.api_key.len()),
56 )
57 .field("site", &self.site)
58 .field("skip_tls_verify", &self.skip_tls_verify)
59 .field("legacy", &self.legacy)
60 .field("request_timeout", &self.request_timeout)
61 .finish()
62 }
63}
64
65mod duration_secs {
70 use std::time::Duration;
71
72 use serde::{Deserialize, Deserializer, Serializer};
73
74 pub fn serialize<S: Serializer>(value: &Duration, serializer: S) -> Result<S::Ok, S::Error> {
75 serializer.serialize_u64(value.as_secs())
76 }
77
78 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Duration, D::Error> {
79 Ok(Duration::from_secs(u64::deserialize(deserializer)?))
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn debug_redacts_api_key() {
89 let cfg = UnifiConfig {
90 api_key: "super-secret-key".to_string(),
91 ..UnifiConfig::default()
92 };
93
94 let debug = format!("{cfg:?}");
95
96 assert!(!debug.contains("super-secret-key"));
97 assert!(debug.contains("redacted"));
98 }
99
100 #[test]
101 fn request_timeout_serializes_as_whole_seconds() {
102 let cfg = UnifiConfig {
103 request_timeout: Duration::from_secs(90),
104 ..UnifiConfig::default()
105 };
106
107 let json = serde_json::to_value(&cfg).unwrap();
108
109 assert_eq!(json["request_timeout"], serde_json::json!(90));
110 }
111
112 #[test]
113 fn request_timeout_round_trips_through_json() {
114 let cfg = UnifiConfig {
115 request_timeout: Duration::from_secs(90),
116 ..UnifiConfig::default()
117 };
118
119 let json = serde_json::to_string(&cfg).unwrap();
120 let restored: UnifiConfig = serde_json::from_str(&json).unwrap();
121
122 assert_eq!(restored.request_timeout, Duration::from_secs(90));
123 }
124}