1use serde::{Deserialize, Serialize};
23use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
24use std::sync::Arc;
25use std::time::{Duration, Instant};
26
27#[derive(Clone)]
31pub struct ServerStatus {
32 start_time: Arc<Instant>,
34 document_count: Arc<AtomicUsize>,
36 request_count: Arc<AtomicU64>,
38 error_count: Arc<AtomicU64>,
40 completion_count: Arc<AtomicU64>,
42 hover_count: Arc<AtomicU64>,
44 diagnostic_count: Arc<AtomicU64>,
46}
47
48impl ServerStatus {
49 pub fn new() -> Self {
51 Self {
52 start_time: Arc::new(Instant::now()),
53 document_count: Arc::new(AtomicUsize::new(0)),
54 request_count: Arc::new(AtomicU64::new(0)),
55 error_count: Arc::new(AtomicU64::new(0)),
56 completion_count: Arc::new(AtomicU64::new(0)),
57 hover_count: Arc::new(AtomicU64::new(0)),
58 diagnostic_count: Arc::new(AtomicU64::new(0)),
59 }
60 }
61
62 pub fn increment_document_count(&self) {
64 self.document_count.fetch_add(1, Ordering::Relaxed);
65 }
66
67 pub fn decrement_document_count(&self) {
69 self.document_count.fetch_sub(1, Ordering::Relaxed);
70 }
71
72 pub fn record_request(&self) {
74 self.request_count.fetch_add(1, Ordering::Relaxed);
75 }
76
77 pub fn record_error(&self) {
79 self.error_count.fetch_add(1, Ordering::Relaxed);
80 }
81
82 pub fn record_completion(&self) {
84 self.completion_count.fetch_add(1, Ordering::Relaxed);
85 }
86
87 pub fn record_hover(&self) {
89 self.hover_count.fetch_add(1, Ordering::Relaxed);
90 }
91
92 pub fn record_diagnostic(&self) {
94 self.diagnostic_count.fetch_add(1, Ordering::Relaxed);
95 }
96
97 pub fn uptime(&self) -> Duration {
99 self.start_time.elapsed()
100 }
101
102 pub fn get_metrics(&self) -> ServerMetrics {
104 let uptime = self.uptime();
105
106 ServerMetrics {
107 uptime_seconds: uptime.as_secs(),
108 document_count: self.document_count.load(Ordering::Relaxed),
109 request_count: self.request_count.load(Ordering::Relaxed),
110 error_count: self.error_count.load(Ordering::Relaxed),
111 completion_count: self.completion_count.load(Ordering::Relaxed),
112 hover_count: self.hover_count.load(Ordering::Relaxed),
113 diagnostic_count: self.diagnostic_count.load(Ordering::Relaxed),
114 requests_per_second: if uptime.as_secs() > 0 {
115 self.request_count.load(Ordering::Relaxed) as f64 / uptime.as_secs() as f64
116 } else {
117 0.0
118 },
119 error_rate: if self.request_count.load(Ordering::Relaxed) > 0 {
120 self.error_count.load(Ordering::Relaxed) as f64
121 / self.request_count.load(Ordering::Relaxed) as f64
122 } else {
123 0.0
124 },
125 }
126 }
127
128 #[cfg(test)]
130 pub fn reset(&self) {
131 self.document_count.store(0, Ordering::Relaxed);
132 self.request_count.store(0, Ordering::Relaxed);
133 self.error_count.store(0, Ordering::Relaxed);
134 self.completion_count.store(0, Ordering::Relaxed);
135 self.hover_count.store(0, Ordering::Relaxed);
136 self.diagnostic_count.store(0, Ordering::Relaxed);
137 }
138}
139
140impl Default for ServerStatus {
141 fn default() -> Self {
142 Self::new()
143 }
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct ServerMetrics {
151 pub uptime_seconds: u64,
153 pub document_count: usize,
155 pub request_count: u64,
157 pub error_count: u64,
159 pub completion_count: u64,
161 pub hover_count: u64,
163 pub diagnostic_count: u64,
165 pub requests_per_second: f64,
167 pub error_rate: f64,
169}
170
171impl ServerMetrics {
172 pub fn format(&self) -> String {
174 format!(
175 "Server Status:\n\
176 - Uptime: {}s\n\
177 - Documents: {}\n\
178 - Requests: {} ({:.2} req/s)\n\
179 - Errors: {} ({:.2}% error rate)\n\
180 - Completions: {}\n\
181 - Hovers: {}\n\
182 - Diagnostics: {}",
183 self.uptime_seconds,
184 self.document_count,
185 self.request_count,
186 self.requests_per_second,
187 self.error_count,
188 self.error_rate * 100.0,
189 self.completion_count,
190 self.hover_count,
191 self.diagnostic_count
192 )
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use std::thread;
200 use std::time::Duration;
201
202 #[test]
203 fn test_server_status_creation() {
204 let status = ServerStatus::new();
205 let metrics = status.get_metrics();
206
207 assert_eq!(metrics.document_count, 0);
208 assert_eq!(metrics.request_count, 0);
209 assert_eq!(metrics.error_count, 0);
210 assert_eq!(metrics.completion_count, 0);
211 assert_eq!(metrics.hover_count, 0);
212 assert_eq!(metrics.diagnostic_count, 0);
213 }
214
215 #[test]
216 fn test_document_count() {
217 let status = ServerStatus::new();
218
219 status.increment_document_count();
220 status.increment_document_count();
221 assert_eq!(status.get_metrics().document_count, 2);
222
223 status.decrement_document_count();
224 assert_eq!(status.get_metrics().document_count, 1);
225 }
226
227 #[test]
228 fn test_request_tracking() {
229 let status = ServerStatus::new();
230
231 status.record_request();
232 status.record_request();
233 status.record_request();
234
235 let metrics = status.get_metrics();
236 assert_eq!(metrics.request_count, 3);
237 }
238
239 #[test]
240 fn test_error_tracking() {
241 let status = ServerStatus::new();
242
243 status.record_request();
244 status.record_request();
245 status.record_error();
246
247 let metrics = status.get_metrics();
248 assert_eq!(metrics.error_count, 1);
249 assert_eq!(metrics.error_rate, 0.5); }
251
252 #[test]
253 fn test_completion_tracking() {
254 let status = ServerStatus::new();
255
256 status.record_completion();
257 status.record_completion();
258
259 let metrics = status.get_metrics();
260 assert_eq!(metrics.completion_count, 2);
261 }
262
263 #[test]
264 fn test_hover_tracking() {
265 let status = ServerStatus::new();
266
267 status.record_hover();
268 status.record_hover();
269 status.record_hover();
270
271 let metrics = status.get_metrics();
272 assert_eq!(metrics.hover_count, 3);
273 }
274
275 #[test]
276 fn test_diagnostic_tracking() {
277 let status = ServerStatus::new();
278
279 status.record_diagnostic();
280
281 let metrics = status.get_metrics();
282 assert_eq!(metrics.diagnostic_count, 1);
283 }
284
285 #[test]
286 fn test_uptime() {
287 let status = ServerStatus::new();
288
289 thread::sleep(Duration::from_millis(100));
291
292 let uptime = status.uptime();
293 assert!(uptime.as_millis() >= 100);
294 }
295
296 #[test]
297 fn test_requests_per_second() {
298 let status = ServerStatus::new();
299
300 thread::sleep(Duration::from_secs(1));
302
303 status.record_request();
304 status.record_request();
305
306 let metrics = status.get_metrics();
307 assert!(metrics.requests_per_second > 0.0);
309 assert!(metrics.requests_per_second <= 2.0);
310 }
311
312 #[test]
313 fn test_metrics_format() {
314 let status = ServerStatus::new();
315
316 status.increment_document_count();
317 status.record_request();
318 status.record_completion();
319
320 let metrics = status.get_metrics();
321 let formatted = metrics.format();
322
323 assert!(formatted.contains("Server Status:"));
324 assert!(formatted.contains("Documents: 1"));
325 assert!(formatted.contains("Requests: 1"));
326 assert!(formatted.contains("Completions: 1"));
327 }
328
329 #[test]
330 fn test_concurrent_updates() {
331 let status = ServerStatus::new();
332 let status_clone = status.clone();
333
334 let handle1 = thread::spawn(move || {
336 for _ in 0..100 {
337 status_clone.record_request();
338 }
339 });
340
341 let status_clone2 = status.clone();
342 let handle2 = thread::spawn(move || {
343 for _ in 0..100 {
344 status_clone2.record_request();
345 }
346 });
347
348 handle1.join().unwrap();
349 handle2.join().unwrap();
350
351 let metrics = status.get_metrics();
352 assert_eq!(metrics.request_count, 200);
353 }
354
355 #[test]
356 fn test_zero_division_safety() {
357 let status = ServerStatus::new();
358
359 let metrics = status.get_metrics();
361 assert_eq!(metrics.error_rate, 0.0);
362
363 assert!(metrics.requests_per_second >= 0.0);
367 }
368
369 #[test]
370 fn test_reset() {
371 let status = ServerStatus::new();
372
373 status.increment_document_count();
374 status.record_request();
375 status.record_error();
376
377 status.reset();
378
379 let metrics = status.get_metrics();
380 assert_eq!(metrics.document_count, 0);
381 assert_eq!(metrics.request_count, 0);
382 assert_eq!(metrics.error_count, 0);
383 }
384}