par_term_config/
profile.rs1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9fn default_refresh_interval_secs() -> u64 {
12 1800
13}
14
15fn default_max_size_bytes() -> usize {
16 1_048_576
17}
18
19fn default_fetch_timeout_secs() -> u64 {
20 10
21}
22
23fn default_true() -> bool {
24 true
25}
26
27#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
29#[serde(rename_all = "snake_case")]
30pub enum ConflictResolution {
31 #[default]
33 LocalWins,
34 RemoteWins,
36}
37
38impl ConflictResolution {
39 pub fn variants() -> &'static [ConflictResolution] {
41 &[
42 ConflictResolution::LocalWins,
43 ConflictResolution::RemoteWins,
44 ]
45 }
46
47 pub fn display_name(&self) -> &'static str {
49 match self {
50 ConflictResolution::LocalWins => "Local Wins",
51 ConflictResolution::RemoteWins => "Remote Wins",
52 }
53 }
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
58pub struct DynamicProfileSource {
59 pub url: String,
61
62 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
64 pub headers: HashMap<String, String>,
65
66 #[serde(default = "default_refresh_interval_secs")]
68 pub refresh_interval_secs: u64,
69
70 #[serde(default = "default_max_size_bytes")]
72 pub max_size_bytes: usize,
73
74 #[serde(default = "default_fetch_timeout_secs")]
76 pub fetch_timeout_secs: u64,
77
78 #[serde(default = "default_true")]
80 pub enabled: bool,
81
82 #[serde(default)]
84 pub conflict_resolution: ConflictResolution,
85
86 #[serde(skip)]
92 pub allow_http: bool,
93}
94
95impl Default for DynamicProfileSource {
96 fn default() -> Self {
97 Self {
98 url: String::new(),
99 headers: HashMap::new(),
100 refresh_interval_secs: default_refresh_interval_secs(),
101 max_size_bytes: default_max_size_bytes(),
102 fetch_timeout_secs: default_fetch_timeout_secs(),
103 enabled: true,
104 conflict_resolution: ConflictResolution::default(),
105 allow_http: false,
106 }
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_default_source() {
116 let source = DynamicProfileSource::default();
117
118 assert_eq!(source.url, "");
119 assert!(source.headers.is_empty());
120 assert_eq!(source.refresh_interval_secs, 1800);
121 assert_eq!(source.max_size_bytes, 1_048_576);
122 assert_eq!(source.fetch_timeout_secs, 10);
123 assert!(source.enabled);
124 assert_eq!(source.conflict_resolution, ConflictResolution::LocalWins);
125 }
126
127 #[test]
128 fn test_serialize_deserialize_roundtrip() {
129 let mut headers = HashMap::new();
130 headers.insert("Authorization".to_string(), "Bearer tok123".to_string());
131 headers.insert("X-Custom".to_string(), "value".to_string());
132
133 let source = DynamicProfileSource {
134 url: "https://example.com/profiles.yaml".to_string(),
135 headers,
136 refresh_interval_secs: 900,
137 max_size_bytes: 512_000,
138 fetch_timeout_secs: 15,
139 enabled: false,
140 conflict_resolution: ConflictResolution::RemoteWins,
141 allow_http: false,
142 };
143
144 let yaml = serde_yaml_ng::to_string(&source).expect("serialize");
145 let deserialized: DynamicProfileSource =
146 serde_yaml_ng::from_str(&yaml).expect("deserialize");
147
148 assert_eq!(deserialized.url, source.url);
149 assert_eq!(deserialized.headers, source.headers);
150 assert_eq!(
151 deserialized.refresh_interval_secs,
152 source.refresh_interval_secs
153 );
154 assert_eq!(deserialized.max_size_bytes, source.max_size_bytes);
155 assert_eq!(deserialized.fetch_timeout_secs, source.fetch_timeout_secs);
156 assert_eq!(deserialized.enabled, source.enabled);
157 assert_eq!(deserialized.conflict_resolution, source.conflict_resolution);
158 }
159
160 #[test]
161 fn test_deserialize_minimal_yaml() {
162 let yaml = "url: https://example.com/profiles.yaml\n";
163 let source: DynamicProfileSource =
164 serde_yaml_ng::from_str(yaml).expect("deserialize minimal");
165
166 assert_eq!(source.url, "https://example.com/profiles.yaml");
167 assert!(source.headers.is_empty());
168 assert_eq!(source.refresh_interval_secs, 1800);
169 assert_eq!(source.max_size_bytes, 1_048_576);
170 assert_eq!(source.fetch_timeout_secs, 10);
171 assert!(source.enabled);
172 assert_eq!(source.conflict_resolution, ConflictResolution::LocalWins);
173 }
174
175 #[test]
176 fn test_conflict_resolution_display() {
177 assert_eq!(ConflictResolution::LocalWins.display_name(), "Local Wins");
178 assert_eq!(ConflictResolution::RemoteWins.display_name(), "Remote Wins");
179 }
180
181 #[test]
182 fn test_conflict_resolution_variants() {
183 let variants = ConflictResolution::variants();
184 assert_eq!(variants.len(), 2);
185 assert_eq!(variants[0], ConflictResolution::LocalWins);
186 assert_eq!(variants[1], ConflictResolution::RemoteWins);
187 }
188}