1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7#[non_exhaustive]
8pub struct ServerConfig {
9 pub id: String,
11 pub name: String,
13 pub host: String,
15 pub port: u16,
17 pub ssl: bool,
19 pub ssl_verify: bool,
21 pub username: Option<String>,
23 pub password: Option<String>,
25 pub connections: u16,
27 pub priority: u8,
29 pub enabled: bool,
31 pub retention: u32,
33 pub pipelining: u8,
35 pub optional: bool,
37 #[serde(default)]
39 pub compress: bool,
40 #[serde(default)]
43 pub ramp_up_delay_ms: u32,
44 #[serde(default = "default_recv_buffer_size")]
46 pub recv_buffer_size: u32,
47 #[serde(default)]
49 pub proxy_url: Option<String>,
50}
51
52fn default_recv_buffer_size() -> u32 {
54 2 * 1024 * 1024
55}
56
57impl ServerConfig {
58 pub fn new(id: impl Into<String>, host: impl Into<String>) -> Self {
60 Self {
61 id: id.into(),
62 host: host.into(),
63 ..Self::default()
64 }
65 }
66}
67
68impl Default for ServerConfig {
69 fn default() -> Self {
70 Self {
71 id: uuid::Uuid::new_v4().to_string(),
72 name: String::new(),
73 host: String::new(),
74 port: 563,
75 ssl: true,
76 ssl_verify: true,
77 username: None,
78 password: None,
79 connections: 8,
80 priority: 0,
81 enabled: true,
82 retention: 0,
83 pipelining: 1,
84 optional: false,
85 compress: false,
86 ramp_up_delay_ms: 250,
87 recv_buffer_size: default_recv_buffer_size(),
88 proxy_url: None,
89 }
90 }
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct ListActiveEntry {
98 pub name: String,
100 pub high: u64,
102 pub low: u64,
104 pub status: String,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct Article {
111 pub message_id: String,
113 pub segment_number: u32,
115 pub bytes: u64,
117 pub downloaded: bool,
119 pub data_begin: Option<u64>,
121 pub data_size: Option<u64>,
123 pub crc32: Option<u32>,
125 pub tried_servers: Vec<String>,
127 pub tries: u32,
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_article_serde_roundtrip() {
137 let article = Article {
138 message_id: "abc123@example.com".to_string(),
139 segment_number: 1,
140 bytes: 500_000,
141 downloaded: false,
142 data_begin: Some(0),
143 data_size: Some(499_000),
144 crc32: Some(0xDEADBEEF),
145 tried_servers: vec!["server1".to_string()],
146 tries: 2,
147 };
148
149 let json = serde_json::to_string(&article).unwrap();
150 let deserialized: Article = serde_json::from_str(&json).unwrap();
151
152 assert_eq!(deserialized.message_id, "abc123@example.com");
153 assert_eq!(deserialized.segment_number, 1);
154 assert_eq!(deserialized.bytes, 500_000);
155 assert!(!deserialized.downloaded);
156 assert_eq!(deserialized.data_begin, Some(0));
157 assert_eq!(deserialized.data_size, Some(499_000));
158 assert_eq!(deserialized.crc32, Some(0xDEADBEEF));
159 assert_eq!(deserialized.tried_servers, vec!["server1"]);
160 assert_eq!(deserialized.tries, 2);
161 }
162
163 #[test]
164 fn test_server_config_serde_roundtrip() {
165 let config = ServerConfig {
166 id: "srv1".to_string(),
167 name: "My Server".to_string(),
168 host: "news.example.com".to_string(),
169 port: 563,
170 ssl: true,
171 ssl_verify: true,
172 username: Some("user".to_string()),
173 password: Some("pass".to_string()),
174 connections: 8,
175 priority: 0,
176 enabled: true,
177 retention: 3000,
178 pipelining: 1,
179 optional: false,
180 compress: true,
181 ramp_up_delay_ms: 500,
182 recv_buffer_size: 2 * 1024 * 1024,
183 proxy_url: Some("socks5://proxy:1080".to_string()),
184 };
185
186 let toml_str = toml::to_string(&config).unwrap();
187 let deserialized: ServerConfig = toml::from_str(&toml_str).unwrap();
188
189 assert_eq!(deserialized.id, "srv1");
190 assert_eq!(deserialized.host, "news.example.com");
191 assert_eq!(deserialized.port, 563);
192 assert!(deserialized.ssl);
193 assert_eq!(deserialized.connections, 8);
194 assert_eq!(deserialized.retention, 3000);
195 assert!(deserialized.compress);
196 assert_eq!(
197 deserialized.proxy_url,
198 Some("socks5://proxy:1080".to_string())
199 );
200 }
201
202 #[test]
203 fn test_server_config_defaults() {
204 let config = ServerConfig::default();
205 assert!(!config.id.is_empty());
207 assert!(uuid::Uuid::parse_str(&config.id).is_ok());
208 assert_eq!(config.port, 563);
209 assert!(config.ssl);
210 assert!(config.ssl_verify);
211 assert_eq!(config.connections, 8);
212 assert_eq!(config.priority, 0);
213 assert!(config.enabled);
214 assert_eq!(config.retention, 0);
215 assert_eq!(config.pipelining, 1);
216 assert!(!config.optional);
217 assert!(!config.compress);
218 assert_eq!(config.ramp_up_delay_ms, 250);
219 assert!(config.proxy_url.is_none());
220 }
221
222 #[test]
223 fn test_list_active_entry_serde() {
224 let entry = ListActiveEntry {
225 name: "alt.binaries.test".to_string(),
226 high: 1_000_000,
227 low: 1,
228 status: "y".to_string(),
229 };
230
231 let json = serde_json::to_string(&entry).unwrap();
232 let deserialized: ListActiveEntry = serde_json::from_str(&json).unwrap();
233
234 assert_eq!(deserialized.name, "alt.binaries.test");
235 assert_eq!(deserialized.high, 1_000_000);
236 assert_eq!(deserialized.low, 1);
237 assert_eq!(deserialized.status, "y");
238 }
239}