Skip to content

Commit 907a9cd

Browse files
committed
feat: add core modules for caching and monitoring
1 parent df9c7c0 commit 907a9cd

File tree

4 files changed

+461
-19
lines changed

4 files changed

+461
-19
lines changed

crates/core/src/cache.rs

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
use dashmap::DashMap;
2+
use serde::{Deserialize, Serialize};
3+
use std::collections::HashMap;
4+
use std::fs;
5+
use std::path::Path;
6+
use std::sync::Mutex;
7+
8+
/// Trait for caching analysis results.
9+
/// Key is typically a file path or module identifier.
10+
/// Value is the serialized analysis result.
11+
pub trait Cache<K, V>: Send + Sync
12+
where
13+
K: Eq + std::hash::Hash + Clone + Send + Sync,
14+
V: Clone + Send + Sync,
15+
{
16+
fn get(&self, key: &K) -> Option<V>;
17+
fn set(&self, key: K, value: V);
18+
fn clear(&self);
19+
}
20+
21+
/// In-memory cache using DashMap for concurrent access.
22+
pub struct InMemoryCache<K, V> {
23+
map: DashMap<K, V>,
24+
}
25+
26+
impl<K, V> InMemoryCache<K, V>
27+
where
28+
K: Eq + std::hash::Hash + Clone + Send + Sync,
29+
V: Clone + Send + Sync,
30+
{
31+
pub fn new() -> Self {
32+
Self {
33+
map: DashMap::new(),
34+
}
35+
}
36+
}
37+
38+
impl<K, V> Default for InMemoryCache<K, V>
39+
where
40+
K: Eq + std::hash::Hash + Clone + Send + Sync,
41+
V: Clone + Send + Sync,
42+
{
43+
fn default() -> Self {
44+
Self::new()
45+
}
46+
}
47+
}
48+
49+
impl<K, V> Default for InMemoryCache<K, V>
50+
where
51+
K: Eq + std::hash::Hash + Clone + Send + Sync,
52+
V: Clone + Send + Sync,
53+
{
54+
fn default() -> Self {
55+
Self::new()
56+
}
57+
}
58+
Self {
59+
map: DashMap::new(),
60+
}
61+
}
62+
}
63+
64+
impl<K, V> Cache<K, V> for InMemoryCache<K, V>
65+
where
66+
K: Eq + std::hash::Hash + Clone + Send + Sync,
67+
V: Clone + Send + Sync,
68+
{
69+
fn get(&self, key: &K) -> Option<V> {
70+
self.map.get(key).map(|v| v.clone())
71+
}
72+
73+
fn set(&self, key: K, value: V) {
74+
self.map.insert(key, value);
75+
}
76+
77+
fn clear(&self) {
78+
self.map.clear();
79+
}
80+
}
81+
82+
/// File-based cache that stores data in a directory.
83+
/// Uses JSON serialization. Stores key-value pairs in files.
84+
#[derive(Serialize, Deserialize)]
85+
struct CacheEntry<K, V> {
86+
key: K,
87+
value: V,
88+
}
89+
90+
pub struct FileCache<K, V> {
91+
dir: String,
92+
cache: Mutex<HashMap<K, V>>,
93+
}
94+
95+
impl<K, V> FileCache<K, V>
96+
where
97+
K: Eq + std::hash::Hash + Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync,
98+
V: Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync,
99+
{
100+
pub fn new(dir: &str) -> Self {
101+
let mut cache = HashMap::new();
102+
if Path::new(dir).exists() {
103+
// Load existing cache from files
104+
if let Ok(entries) = fs::read_dir(dir) {
105+
for entry in entries.flatten() {
106+
let path = entry.path();
107+
if path.extension() == Some(std::ffi::OsStr::new("json")) {
108+
if let Ok(content) = fs::read_to_string(&path) {
109+
if let Ok(entry) = serde_json::from_str::<CacheEntry<K, V>>(&content) {
110+
cache.insert(entry.key, entry.value);
111+
}
112+
}
113+
}
114+
}
115+
}
116+
} else {
117+
fs::create_dir_all(dir).ok();
118+
}
119+
Self {
120+
dir: dir.to_string(),
121+
cache: Mutex::new(cache),
122+
}
123+
}
124+
125+
fn key_to_filename(&self, key: &K) -> String {
126+
// Simple hash for filename
127+
use std::collections::hash_map::DefaultHasher;
128+
use std::hash::Hasher;
129+
let mut hasher = DefaultHasher::new();
130+
key.hash(&mut hasher);
131+
format!("{:x}.json", hasher.finish())
132+
}
133+
}
134+
135+
impl<K, V> Cache<K, V> for FileCache<K, V>
136+
where
137+
K: Eq + std::hash::Hash + Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync,
138+
V: Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync,
139+
{
140+
fn get(&self, key: &K) -> Option<V> {
141+
let cache = self.cache.lock().unwrap();
142+
cache.get(key).cloned()
143+
}
144+
145+
fn set(&self, key: K, value: V) {
146+
let mut cache = self.cache.lock().unwrap();
147+
cache.insert(key.clone(), value.clone());
148+
// Persist to file
149+
let filename = self.key_to_filename(&key);
150+
let file_path = Path::new(&self.dir).join(filename);
151+
let entry = CacheEntry { key, value };
152+
if let Ok(json) = serde_json::to_string(&entry) {
153+
fs::write(file_path, json).ok();
154+
}
155+
}
156+
157+
fn clear(&self) {
158+
let mut cache = self.cache.lock().unwrap();
159+
cache.clear();
160+
fs::remove_dir_all(&self.dir).ok();
161+
fs::create_dir_all(&self.dir).ok();
162+
}
163+
}
164+
165+
/// Example analysis result structure.
166+
/// This can be customized based on what agents analyze.
167+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
168+
pub struct AnalysisResult {
169+
pub file_path: String,
170+
pub issues: Vec<String>,
171+
pub metrics: HashMap<String, f64>,
172+
}
173+
174+
#[cfg(test)]
175+
mod tests {
176+
use super::*;
177+
178+
#[test]
179+
fn test_in_memory_cache() {
180+
let cache = InMemoryCache::new();
181+
let key = "test.rs";
182+
let value = AnalysisResult {
183+
file_path: key.to_string(),
184+
issues: vec!["TODO".to_string()],
185+
metrics: [("loc".to_string(), 100.0)].into(),
186+
};
187+
cache.set(key.to_string(), value.clone());
188+
assert_eq!(cache.get(&key.to_string()), Some(value));
189+
cache.clear();
190+
assert_eq!(cache.get(&key.to_string()), None);
191+
}
192+
193+
#[test]
194+
fn test_file_cache() {
195+
let temp_dir = tempfile::tempdir().unwrap();
196+
let cache_dir = temp_dir.path().to_string_lossy().to_string();
197+
let cache = FileCache::new(&cache_dir);
198+
let key = "test.rs".to_string();
199+
let value = AnalysisResult {
200+
file_path: key.clone(),
201+
issues: vec!["FIXME".to_string()],
202+
metrics: [("complexity".to_string(), 5.0)].into(),
203+
};
204+
cache.set(key.clone(), value.clone());
205+
// Simulate new instance to test persistence
206+
let cache2 = FileCache::new(&cache_dir);
207+
assert_eq!(cache2.get(&key), Some(value));
208+
}
209+
}

crates/core/src/distributed.rs

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
use crate::{Match, PatternDetector};
1+
use crate::{Match, PatternDetector, PerformanceMonitor};
22
use anyhow::Result;
33
use serde::{Deserialize, Serialize};
44
use std::collections::HashMap;
55
use std::path::PathBuf;
6+
use std::sync::Arc;
7+
use tokio::sync::Mutex;
8+
use tracing::{info, warn};
69

710
use std::time::Instant;
811

@@ -39,12 +42,13 @@ pub struct WorkerConfig {
3942
pub endpoint: Option<String>, // For remote workers
4043
}
4144

42-
/// Distributed scan coordinator
45+
/// Distributed scan coordinator with performance monitoring
4346
pub struct DistributedCoordinator {
4447
workers: Vec<WorkerConfig>,
4548
work_queue: Vec<WorkUnit>,
4649
completed_work: HashMap<String, WorkResult>,
4750
detectors: HashMap<String, Box<dyn PatternDetector>>,
51+
monitor: Arc<Mutex<PerformanceMonitor>>,
4852
}
4953

5054
impl DistributedCoordinator {
@@ -54,12 +58,13 @@ impl DistributedCoordinator {
5458
work_queue: Vec::new(),
5559
completed_work: HashMap::new(),
5660
detectors: HashMap::new(),
61+
monitor: Arc::new(Mutex::new(PerformanceMonitor::new())),
5762
}
5863
}
5964

6065
/// Register a worker node
6166
pub fn register_worker(&mut self, config: WorkerConfig) {
62-
println!(
67+
info!(
6368
"🤖 Registered worker: {} (cores: {}, memory: {}MB)",
6469
config.worker_id, config.cpu_cores, config.memory_limit_mb
6570
);
@@ -90,33 +95,39 @@ impl DistributedCoordinator {
9095
// Sort by priority (higher priority first)
9196
self.work_queue.sort_by(|a, b| b.priority.cmp(&a.priority));
9297

93-
println!(
98+
info!(
9499
"📦 Created {} work units from {} files",
95100
self.work_queue.len(),
96101
files.len()
97102
);
98103
Ok(())
99104
}
100105

101-
/// Distribute and execute work units
102-
pub fn execute_distributed_scan(&mut self) -> Result<Vec<Match>> {
106+
/// Distribute and execute work units with performance monitoring
107+
pub async fn execute_distributed_scan(&mut self) -> Result<Vec<Match>> {
103108
let start_time = Instant::now();
104109
let total_units = self.work_queue.len();
105110

106-
println!(
111+
info!(
107112
"🚀 Starting distributed scan with {} workers and {} work units",
108113
self.workers.len(),
109114
total_units
110115
);
111116

117+
// Start monitoring
118+
{
119+
let mut monitor = self.monitor.lock().await;
120+
monitor.start_operation("distributed_scan");
121+
}
122+
112123
if self.workers.is_empty() {
113124
// Fallback to local processing
114-
return self.execute_local_fallback();
125+
return self.execute_local_fallback().await;
115126
}
116127

117128
// Simulate distributed processing (in real implementation, this would use
118129
// actual network communication, message queues, etc.)
119-
self.simulate_distributed_execution()?;
130+
self.simulate_distributed_execution().await?;
120131

121132
let total_matches: Vec<Match> = self
122133
.completed_work
@@ -127,6 +138,12 @@ impl DistributedCoordinator {
127138
let duration = start_time.elapsed();
128139
self.print_execution_summary(duration, total_matches.len());
129140

141+
// End monitoring
142+
{
143+
let mut monitor = self.monitor.lock().await;
144+
monitor.end_operation("distributed_scan").await?;
145+
}
146+
130147
Ok(total_matches)
131148
}
132149

@@ -179,7 +196,7 @@ impl DistributedCoordinator {
179196
}
180197
}
181198

182-
fn simulate_distributed_execution(&mut self) -> Result<()> {
199+
async fn simulate_distributed_execution(&mut self) -> Result<()> {
183200
use rayon::prelude::*;
184201

185202
// Process work units in parallel (simulating distributed workers)
@@ -239,8 +256,8 @@ impl DistributedCoordinator {
239256
})
240257
}
241258

242-
fn execute_local_fallback(&mut self) -> Result<Vec<Match>> {
243-
println!("⚠️ No workers available, falling back to local processing");
259+
async fn execute_local_fallback(&mut self) -> Result<Vec<Match>> {
260+
warn!("⚠️ No workers available, falling back to local processing");
244261

245262
let mut all_matches = Vec::new();
246263
for unit in &self.work_queue {
@@ -292,18 +309,18 @@ impl DistributedCoordinator {
292309
}
293310

294311
fn print_execution_summary(&self, duration: std::time::Duration, total_matches: usize) {
295-
println!("✅ Distributed scan completed!");
296-
println!(" Duration: {:?}", duration);
297-
println!(" Total matches: {}", total_matches);
298-
println!(" Work units processed: {}", self.completed_work.len());
312+
info!("✅ Distributed scan completed!");
313+
info!(" Duration: {:?}", duration);
314+
info!(" Total matches: {}", total_matches);
315+
info!(" Work units processed: {}", self.completed_work.len());
299316

300317
let stats = self.get_statistics();
301-
println!(" Files processed: {}", stats.total_files_processed);
302-
println!(" Average unit size: {:.1} files", stats.average_unit_size);
318+
info!(" Files processed: {}", stats.total_files_processed);
319+
info!(" Average unit size: {:.1} files", stats.average_unit_size);
303320

304321
// Show worker utilization
305322
for (worker_id, utilization) in &stats.worker_utilization {
306-
println!(" {}: {:.1}% utilization", worker_id, utilization * 100.0);
323+
info!(" {}: {:.1}% utilization", worker_id, utilization * 100.0);
307324
}
308325
}
309326
}

crates/core/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ use ignore::WalkBuilder;
44
use rayon::prelude::*;
55
use std::path::Path;
66

7+
pub mod cache;
78
pub mod config;
89
pub mod custom_detectors;
910
pub mod detector_factory;
1011
pub mod detectors;
1112
pub mod distributed;
1213
pub mod enhanced_config;
1314
pub mod incremental;
15+
pub mod monitoring;
1416
pub mod optimized_scanner;
1517
pub mod performance;
1618

@@ -126,12 +128,14 @@ impl Scanner {
126128
}
127129

128130
// Re-export detectors and factory for convenience
131+
pub use cache::*;
129132
pub use custom_detectors::*;
130133
pub use detector_factory::*;
131134
pub use detectors::*;
132135
pub use distributed::*;
133136
pub use enhanced_config::*;
134137
pub use incremental::*;
138+
pub use monitoring::*;
135139
pub use optimized_scanner::*;
136140
pub use performance::*;
137141

0 commit comments

Comments
 (0)