codegraph_parser_api/
metrics.rs

1use serde::{Deserialize, Serialize};
2use std::time::Duration;
3
4/// Metrics collected during parsing
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6pub struct ParserMetrics {
7    /// Total files attempted to parse
8    pub files_attempted: usize,
9
10    /// Files successfully parsed
11    pub files_succeeded: usize,
12
13    /// Files that failed parsing
14    pub files_failed: usize,
15
16    /// Total time spent parsing
17    #[serde(with = "duration_serde")]
18    pub total_parse_time: Duration,
19
20    /// Total entities extracted
21    pub total_entities: usize,
22
23    /// Total relationships extracted
24    pub total_relationships: usize,
25
26    /// Peak memory usage (if available)
27    pub peak_memory_bytes: Option<usize>,
28}
29
30// Helper module for serializing Duration
31mod duration_serde {
32    use serde::{Deserialize, Deserializer, Serialize, Serializer};
33    use std::time::Duration;
34
35    pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
36    where
37        S: Serializer,
38    {
39        duration.as_secs().serialize(serializer)
40    }
41
42    pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
43    where
44        D: Deserializer<'de>,
45    {
46        let secs: u64 = u64::deserialize(deserializer)?;
47        Ok(Duration::from_secs(secs))
48    }
49}
50
51impl Default for ParserMetrics {
52    fn default() -> Self {
53        Self {
54            files_attempted: 0,
55            files_succeeded: 0,
56            files_failed: 0,
57            total_parse_time: Duration::ZERO,
58            total_entities: 0,
59            total_relationships: 0,
60            peak_memory_bytes: None,
61        }
62    }
63}
64
65impl ParserMetrics {
66    /// Success rate (0.0 to 1.0)
67    pub fn success_rate(&self) -> f64 {
68        if self.files_attempted == 0 {
69            0.0
70        } else {
71            self.files_succeeded as f64 / self.files_attempted as f64
72        }
73    }
74
75    /// Average parse time per file
76    pub fn avg_parse_time(&self) -> Duration {
77        if self.files_succeeded == 0 {
78            Duration::ZERO
79        } else {
80            self.total_parse_time / self.files_succeeded as u32
81        }
82    }
83
84    /// Average entities per file
85    pub fn avg_entities_per_file(&self) -> f64 {
86        if self.files_succeeded == 0 {
87            0.0
88        } else {
89            self.total_entities as f64 / self.files_succeeded as f64
90        }
91    }
92
93    /// Merge another metrics object into this one
94    pub fn merge(&mut self, other: &ParserMetrics) {
95        self.files_attempted += other.files_attempted;
96        self.files_succeeded += other.files_succeeded;
97        self.files_failed += other.files_failed;
98        self.total_parse_time += other.total_parse_time;
99        self.total_entities += other.total_entities;
100        self.total_relationships += other.total_relationships;
101
102        // Take max memory
103        self.peak_memory_bytes = match (self.peak_memory_bytes, other.peak_memory_bytes) {
104            (Some(a), Some(b)) => Some(a.max(b)),
105            (Some(a), None) => Some(a),
106            (None, Some(b)) => Some(b),
107            (None, None) => None,
108        };
109    }
110}