use std::io::Read;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::core::error::{Error, Result};
use crate::core::eventlog::{Event, EventLog, Segment};
pub const METRICS_SNAPSHOT_VERSION: u32 = 1;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MetricsSnapshot {
pub version: u32,
#[serde(default)]
pub git_sha: Option<String>,
#[serde(default)]
pub loc: Option<serde_json::Value>,
#[serde(default)]
pub complexity: Option<serde_json::Value>,
#[serde(default)]
pub churn: Option<serde_json::Value>,
#[serde(default)]
pub change_coupling: Option<serde_json::Value>,
#[serde(default)]
pub duplication: Option<serde_json::Value>,
#[serde(default)]
pub hotspot: Option<serde_json::Value>,
#[serde(default)]
pub delta: Option<serde_json::Value>,
}
impl Default for MetricsSnapshot {
fn default() -> Self {
Self {
version: METRICS_SNAPSHOT_VERSION,
git_sha: None,
loc: None,
complexity: None,
churn: None,
change_coupling: None,
duplication: None,
hotspot: None,
delta: None,
}
}
}
impl MetricsSnapshot {
pub fn latest_in(log: &EventLog) -> Result<Option<(Event, Self)>> {
Self::latest_in_segments(log.segments()?)
}
pub fn latest_in_segments(mut segments: Vec<Segment>) -> Result<Option<(Event, Self)>> {
segments.reverse();
for seg in segments {
let mut buf = String::new();
seg.open()?
.read_to_string(&mut buf)
.map_err(|e| Error::Io {
path: seg.path.clone(),
source: e,
})?;
for line in buf.lines().rev() {
if line.trim().is_empty() {
continue;
}
let Ok(event) = serde_json::from_str::<Event>(line) else {
continue;
};
if let Ok(metrics) = serde_json::from_value::<Self>(event.data.clone()) {
return Ok(Some((event, metrics)));
}
}
}
Ok(None)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct SnapshotDelta {
pub from_sha: Option<String>,
pub from_timestamp: Option<DateTime<Utc>>,
pub complexity: Option<ComplexityDelta>,
pub churn: Option<ChurnDelta>,
pub hotspot: Option<HotspotDelta>,
pub duplication: Option<DuplicationDelta>,
pub change_coupling: Option<ChangeCouplingDelta>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct ComplexityDelta {
pub max_ccn: i64,
pub max_cognitive: i64,
pub functions: i64,
pub files: i64,
pub new_top_ccn: Vec<String>,
pub new_top_cognitive: Vec<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct ChurnDelta {
pub commits_in_window: i64,
pub top_file_changed: bool,
pub previous_top_file: Option<String>,
pub current_top_file: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct HotspotDelta {
pub max_score: f64,
pub top_files_added: Vec<String>,
pub top_files_dropped: Vec<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct DuplicationDelta {
pub duplicate_blocks: i64,
pub duplicate_tokens: i64,
pub files_affected: i64,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct ChangeCouplingDelta {
pub pairs: i64,
pub files: i64,
pub max_pair_count: i64,
}