impl QualityMonitorEngine {
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new(config: QualityMonitorConfig) -> Self {
Self {
config,
watchers: Arc::new(RwLock::new(HashMap::new())),
metrics: Arc::new(RwLock::new(HashMap::new())),
event_sender: None,
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn start_monitoring(
&mut self,
project_id: String,
project_path: PathBuf,
) -> Result<()> {
info!(
"Starting quality monitoring for project: {} at {:?}",
project_id, project_path
);
let (tx, mut rx) = mpsc::channel(100);
let project_id_clone = project_id.clone();
let event_sender = self.event_sender.clone();
let mut watcher = RecommendedWatcher::new(
move |result: notify::Result<Event>| match result {
Ok(event) => {
if let Err(e) = tx.try_send((project_id_clone.clone(), event)) {
warn!("Failed to send file system event: {}", e);
}
}
Err(e) => {
error!("File system watch error: {}", e);
}
},
Config::default(),
)?;
watcher.watch(&project_path, RecursiveMode::Recursive)?;
{
let mut watchers = self.watchers.write().await;
watchers.insert(project_id.clone(), watcher);
}
let metrics = self.metrics.clone();
let config = self.config.clone();
let project_path_clone = project_path.clone();
tokio::spawn(async move {
while let Some((project_id, event)) = rx.recv().await {
if let Err(e) = Self::handle_file_system_event(
&project_id,
event,
&project_path_clone,
&config,
&metrics,
&event_sender,
)
.await
{
error!("Error handling file system event: {}", e);
}
}
});
self.perform_full_analysis(&project_id, &project_path)
.await?;
self.start_periodic_monitoring(project_id.clone(), project_path)
.await?;
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn stop_monitoring(&mut self, project_id: &str) -> Result<()> {
info!("Stopping quality monitoring for project: {}", project_id);
{
let mut watchers = self.watchers.write().await;
watchers.remove(project_id);
}
{
let mut metrics = self.metrics.write().await;
metrics.remove(project_id);
}
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn get_metrics(&self, project_id: &str) -> Option<QualityMetrics> {
let metrics = self.metrics.read().await;
metrics.get(project_id).cloned()
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn set_event_sender(&mut self, sender: mpsc::Sender<QualityEvent>) {
self.event_sender = Some(sender);
}
async fn perform_full_analysis(&self, project_id: &str, _project_path: &Path) -> Result<()> {
info!(
"Performing full quality analysis for project: {}",
project_id
);
let metrics = QualityMetrics {
project_id: project_id.to_string(),
last_updated: SystemTime::now(),
quality_score: 0.85,
files_analyzed: 42,
functions_analyzed: 156,
avg_complexity: 6.8,
max_complexity: 18,
hotspot_functions: 5,
satd_issues: 3,
complexity_distribution: ComplexityDistribution {
low: 89,
medium: 45,
high: 15,
very_high: 5,
violations: 2,
},
file_metrics: HashMap::new(),
quality_trend: 0.02, };
{
let mut metrics_map = self.metrics.write().await;
metrics_map.insert(project_id.to_string(), metrics.clone());
}
if let Some(sender) = &self.event_sender {
let event = QualityEvent::MetricsUpdated {
project_id: project_id.to_string(),
metrics,
changes: vec![], };
if let Err(e) = sender.try_send(event) {
warn!("Failed to send metrics update event: {}", e);
}
}
Ok(())
}
async fn start_periodic_monitoring(
&self,
project_id: String,
_project_path: PathBuf,
) -> Result<()> {
let metrics = self.metrics.clone();
let config = self.config.clone();
let _event_sender = self.event_sender.clone();
tokio::spawn(async move {
let mut interval = interval(config.update_interval);
loop {
interval.tick().await;
debug!("Periodic quality check for project: {}", project_id);
{
let mut metrics_map = metrics.write().await;
if let Some(project_metrics) = metrics_map.get_mut(&project_id) {
project_metrics.last_updated = SystemTime::now();
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
project_id.hash(&mut hasher);
let random_seed = hasher.finish();
let change = ((random_seed % 200) as f64 - 100.0) / 10000.0; project_metrics.quality_score += change;
project_metrics.quality_score =
project_metrics.quality_score.clamp(0.0, 1.0);
}
}
}
});
Ok(())
}
}