use serde::{Deserialize, Serialize};
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
#[derive(Clone)]
pub struct ServerStatus {
start_time: Arc<Instant>,
document_count: Arc<AtomicUsize>,
request_count: Arc<AtomicU64>,
error_count: Arc<AtomicU64>,
completion_count: Arc<AtomicU64>,
hover_count: Arc<AtomicU64>,
diagnostic_count: Arc<AtomicU64>,
}
impl ServerStatus {
pub fn new() -> Self {
Self {
start_time: Arc::new(Instant::now()),
document_count: Arc::new(AtomicUsize::new(0)),
request_count: Arc::new(AtomicU64::new(0)),
error_count: Arc::new(AtomicU64::new(0)),
completion_count: Arc::new(AtomicU64::new(0)),
hover_count: Arc::new(AtomicU64::new(0)),
diagnostic_count: Arc::new(AtomicU64::new(0)),
}
}
pub fn increment_document_count(&self) {
self.document_count.fetch_add(1, Ordering::Relaxed);
}
pub fn decrement_document_count(&self) {
self.document_count.fetch_sub(1, Ordering::Relaxed);
}
pub fn record_request(&self) {
self.request_count.fetch_add(1, Ordering::Relaxed);
}
pub fn record_error(&self) {
self.error_count.fetch_add(1, Ordering::Relaxed);
}
pub fn record_completion(&self) {
self.completion_count.fetch_add(1, Ordering::Relaxed);
}
pub fn record_hover(&self) {
self.hover_count.fetch_add(1, Ordering::Relaxed);
}
pub fn record_diagnostic(&self) {
self.diagnostic_count.fetch_add(1, Ordering::Relaxed);
}
pub fn uptime(&self) -> Duration {
self.start_time.elapsed()
}
pub fn get_metrics(&self) -> ServerMetrics {
let uptime = self.uptime();
ServerMetrics {
uptime_seconds: uptime.as_secs(),
document_count: self.document_count.load(Ordering::Relaxed),
request_count: self.request_count.load(Ordering::Relaxed),
error_count: self.error_count.load(Ordering::Relaxed),
completion_count: self.completion_count.load(Ordering::Relaxed),
hover_count: self.hover_count.load(Ordering::Relaxed),
diagnostic_count: self.diagnostic_count.load(Ordering::Relaxed),
requests_per_second: if uptime.as_secs() > 0 {
self.request_count.load(Ordering::Relaxed) as f64 / uptime.as_secs() as f64
} else {
0.0
},
error_rate: if self.request_count.load(Ordering::Relaxed) > 0 {
self.error_count.load(Ordering::Relaxed) as f64
/ self.request_count.load(Ordering::Relaxed) as f64
} else {
0.0
},
}
}
#[cfg(test)]
pub fn reset(&self) {
self.document_count.store(0, Ordering::Relaxed);
self.request_count.store(0, Ordering::Relaxed);
self.error_count.store(0, Ordering::Relaxed);
self.completion_count.store(0, Ordering::Relaxed);
self.hover_count.store(0, Ordering::Relaxed);
self.diagnostic_count.store(0, Ordering::Relaxed);
}
}
impl Default for ServerStatus {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerMetrics {
pub uptime_seconds: u64,
pub document_count: usize,
pub request_count: u64,
pub error_count: u64,
pub completion_count: u64,
pub hover_count: u64,
pub diagnostic_count: u64,
pub requests_per_second: f64,
pub error_rate: f64,
}
impl ServerMetrics {
pub fn format(&self) -> String {
format!(
"Server Status:\n\
- Uptime: {}s\n\
- Documents: {}\n\
- Requests: {} ({:.2} req/s)\n\
- Errors: {} ({:.2}% error rate)\n\
- Completions: {}\n\
- Hovers: {}\n\
- Diagnostics: {}",
self.uptime_seconds,
self.document_count,
self.request_count,
self.requests_per_second,
self.error_count,
self.error_rate * 100.0,
self.completion_count,
self.hover_count,
self.diagnostic_count
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
use std::time::Duration;
#[test]
fn test_server_status_creation() {
let status = ServerStatus::new();
let metrics = status.get_metrics();
assert_eq!(metrics.document_count, 0);
assert_eq!(metrics.request_count, 0);
assert_eq!(metrics.error_count, 0);
assert_eq!(metrics.completion_count, 0);
assert_eq!(metrics.hover_count, 0);
assert_eq!(metrics.diagnostic_count, 0);
}
#[test]
fn test_document_count() {
let status = ServerStatus::new();
status.increment_document_count();
status.increment_document_count();
assert_eq!(status.get_metrics().document_count, 2);
status.decrement_document_count();
assert_eq!(status.get_metrics().document_count, 1);
}
#[test]
fn test_request_tracking() {
let status = ServerStatus::new();
status.record_request();
status.record_request();
status.record_request();
let metrics = status.get_metrics();
assert_eq!(metrics.request_count, 3);
}
#[test]
fn test_error_tracking() {
let status = ServerStatus::new();
status.record_request();
status.record_request();
status.record_error();
let metrics = status.get_metrics();
assert_eq!(metrics.error_count, 1);
assert_eq!(metrics.error_rate, 0.5); }
#[test]
fn test_completion_tracking() {
let status = ServerStatus::new();
status.record_completion();
status.record_completion();
let metrics = status.get_metrics();
assert_eq!(metrics.completion_count, 2);
}
#[test]
fn test_hover_tracking() {
let status = ServerStatus::new();
status.record_hover();
status.record_hover();
status.record_hover();
let metrics = status.get_metrics();
assert_eq!(metrics.hover_count, 3);
}
#[test]
fn test_diagnostic_tracking() {
let status = ServerStatus::new();
status.record_diagnostic();
let metrics = status.get_metrics();
assert_eq!(metrics.diagnostic_count, 1);
}
#[test]
fn test_uptime() {
let status = ServerStatus::new();
thread::sleep(Duration::from_millis(100));
let uptime = status.uptime();
assert!(uptime.as_millis() >= 100);
}
#[test]
fn test_requests_per_second() {
let status = ServerStatus::new();
thread::sleep(Duration::from_secs(1));
status.record_request();
status.record_request();
let metrics = status.get_metrics();
assert!(metrics.requests_per_second > 0.0);
assert!(metrics.requests_per_second <= 2.0);
}
#[test]
fn test_metrics_format() {
let status = ServerStatus::new();
status.increment_document_count();
status.record_request();
status.record_completion();
let metrics = status.get_metrics();
let formatted = metrics.format();
assert!(formatted.contains("Server Status:"));
assert!(formatted.contains("Documents: 1"));
assert!(formatted.contains("Requests: 1"));
assert!(formatted.contains("Completions: 1"));
}
#[test]
fn test_concurrent_updates() {
let status = ServerStatus::new();
let status_clone = status.clone();
let handle1 = thread::spawn(move || {
for _ in 0..100 {
status_clone.record_request();
}
});
let status_clone2 = status.clone();
let handle2 = thread::spawn(move || {
for _ in 0..100 {
status_clone2.record_request();
}
});
handle1.join().unwrap();
handle2.join().unwrap();
let metrics = status.get_metrics();
assert_eq!(metrics.request_count, 200);
}
#[test]
fn test_zero_division_safety() {
let status = ServerStatus::new();
let metrics = status.get_metrics();
assert_eq!(metrics.error_rate, 0.0);
assert!(metrics.requests_per_second >= 0.0);
}
#[test]
fn test_reset() {
let status = ServerStatus::new();
status.increment_document_count();
status.record_request();
status.record_error();
status.reset();
let metrics = status.get_metrics();
assert_eq!(metrics.document_count, 0);
assert_eq!(metrics.request_count, 0);
assert_eq!(metrics.error_count, 0);
}
}