rusty_files/server/
state.rs

1use crate::SearchEngine;
2use crate::server::config::ServerConfig;
3use crate::server::models::FileChangeEvent;
4use dashmap::DashMap;
5use parking_lot::RwLock;
6use std::path::PathBuf;
7use std::sync::atomic::{AtomicU64, Ordering};
8use std::sync::Arc;
9use std::time::Instant;
10use chrono::{DateTime, Utc};
11use tokio::sync::broadcast;
12
13pub struct AppState {
14    pub engine: Arc<RwLock<SearchEngine>>,
15    pub config: Arc<ServerConfig>,
16    pub metrics: Arc<Metrics>,
17    pub watchers: Arc<DashMap<String, WatchHandle>>,
18    pub event_tx: broadcast::Sender<FileChangeEvent>,
19    pub start_time: Instant,
20}
21
22impl AppState {
23    pub fn new(engine: SearchEngine, config: ServerConfig) -> Self {
24        let (event_tx, _) = broadcast::channel(1000);
25
26        Self {
27            engine: Arc::new(RwLock::new(engine)),
28            config: Arc::new(config),
29            metrics: Arc::new(Metrics::new()),
30            watchers: Arc::new(DashMap::new()),
31            event_tx,
32            start_time: Instant::now(),
33        }
34    }
35
36    pub fn uptime_seconds(&self) -> u64 {
37        self.start_time.elapsed().as_secs()
38    }
39}
40
41pub struct Metrics {
42    pub total_searches: AtomicU64,
43    pub total_search_time_ms: AtomicU64,
44    pub cache_hits: AtomicU64,
45    pub cache_misses: AtomicU64,
46}
47
48impl Metrics {
49    pub fn new() -> Self {
50        Self {
51            total_searches: AtomicU64::new(0),
52            total_search_time_ms: AtomicU64::new(0),
53            cache_hits: AtomicU64::new(0),
54            cache_misses: AtomicU64::new(0),
55        }
56    }
57
58    pub fn record_search(&self, duration_ms: u64) {
59        self.total_searches.fetch_add(1, Ordering::Relaxed);
60        self.total_search_time_ms
61            .fetch_add(duration_ms, Ordering::Relaxed);
62    }
63
64    pub fn avg_search_time_ms(&self) -> f64 {
65        let total = self.total_searches.load(Ordering::Relaxed);
66        if total == 0 {
67            return 0.0;
68        }
69        self.total_search_time_ms.load(Ordering::Relaxed) as f64 / total as f64
70    }
71
72    pub fn cache_hit_rate(&self) -> f32 {
73        let hits = self.cache_hits.load(Ordering::Relaxed);
74        let misses = self.cache_misses.load(Ordering::Relaxed);
75        let total = hits + misses;
76
77        if total == 0 {
78            return 0.0;
79        }
80
81        hits as f32 / total as f32
82    }
83}
84
85pub struct WatchHandle {
86    pub path: PathBuf,
87    pub recursive: bool,
88    pub created_at: DateTime<Utc>,
89}