term_guard/analyzers/
context.rs1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6use super::errors::AnalyzerError;
7use super::types::MetricValue;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct AnalyzerContext {
36 metrics: HashMap<String, MetricValue>,
38
39 metadata: AnalysisMetadata,
41
42 errors: Vec<AnalysisError>,
44}
45
46impl AnalyzerContext {
47 pub fn new() -> Self {
49 Self {
50 metrics: HashMap::new(),
51 metadata: AnalysisMetadata::new(),
52 errors: Vec::new(),
53 }
54 }
55
56 pub fn with_dataset(dataset_name: impl Into<String>) -> Self {
58 Self {
59 metrics: HashMap::new(),
60 metadata: AnalysisMetadata::with_dataset(dataset_name),
61 errors: Vec::new(),
62 }
63 }
64
65 pub fn store_metric(&mut self, key: impl Into<String>, value: MetricValue) {
72 self.metrics.insert(key.into(), value);
73 }
74
75 pub fn store_analyzer_metric(
83 &mut self,
84 analyzer_name: &str,
85 metric_name: &str,
86 value: MetricValue,
87 ) {
88 let key = format!("{analyzer_name}.{metric_name}");
89 self.store_metric(key, value);
90 }
91
92 pub fn get_metric(&self, key: &str) -> Option<&MetricValue> {
94 self.metrics.get(key)
95 }
96
97 pub fn get_analyzer_metrics(&self, analyzer_name: &str) -> HashMap<String, &MetricValue> {
101 let prefix = format!("{analyzer_name}.");
102 self.metrics
103 .iter()
104 .filter(|(k, _)| k.starts_with(&prefix))
105 .map(|(k, v)| {
106 let metric_name = k.strip_prefix(&prefix).unwrap_or(k);
107 (metric_name.to_string(), v)
108 })
109 .collect()
110 }
111
112 pub fn all_metrics(&self) -> &HashMap<String, MetricValue> {
114 &self.metrics
115 }
116
117 pub fn record_error(&mut self, analyzer_name: impl Into<String>, error: AnalyzerError) {
119 self.errors.push(AnalysisError {
120 analyzer_name: analyzer_name.into(),
121 error: error.to_string(),
122 });
123 }
124
125 pub fn errors(&self) -> &[AnalysisError] {
127 &self.errors
128 }
129
130 pub fn has_errors(&self) -> bool {
132 !self.errors.is_empty()
133 }
134
135 pub fn metadata(&self) -> &AnalysisMetadata {
137 &self.metadata
138 }
139
140 pub fn metadata_mut(&mut self) -> &mut AnalysisMetadata {
142 &mut self.metadata
143 }
144
145 pub fn merge(&mut self, other: AnalyzerContext) {
149 self.metrics.extend(other.metrics);
150 self.errors.extend(other.errors);
151 self.metadata.merge(other.metadata);
152 }
153
154 pub fn summary(&self) -> AnalysisSummary {
156 AnalysisSummary {
157 total_metrics: self.metrics.len(),
158 total_errors: self.errors.len(),
159 analyzer_count: self.count_analyzers(),
160 dataset_name: self.metadata.dataset_name.clone(),
161 }
162 }
163
164 fn count_analyzers(&self) -> usize {
166 let mut analyzers = std::collections::HashSet::new();
167 for key in self.metrics.keys() {
168 if let Some(analyzer_name) = key.split('.').next() {
169 analyzers.insert(analyzer_name);
170 }
171 }
172 analyzers.len()
173 }
174}
175
176impl Default for AnalyzerContext {
177 fn default() -> Self {
178 Self::new()
179 }
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct AnalysisMetadata {
185 pub dataset_name: Option<String>,
187
188 pub start_time: Option<chrono::DateTime<chrono::Utc>>,
190
191 pub end_time: Option<chrono::DateTime<chrono::Utc>>,
193
194 pub custom: HashMap<String, String>,
196}
197
198impl AnalysisMetadata {
199 pub fn new() -> Self {
201 Self {
202 dataset_name: None,
203 start_time: None,
204 end_time: None,
205 custom: HashMap::new(),
206 }
207 }
208
209 pub fn with_dataset(name: impl Into<String>) -> Self {
211 Self {
212 dataset_name: Some(name.into()),
213 start_time: None,
214 end_time: None,
215 custom: HashMap::new(),
216 }
217 }
218
219 pub fn record_start(&mut self) {
221 self.start_time = Some(chrono::Utc::now());
222 }
223
224 pub fn record_end(&mut self) {
226 self.end_time = Some(chrono::Utc::now());
227 }
228
229 pub fn duration(&self) -> Option<chrono::Duration> {
231 match (self.start_time, self.end_time) {
232 (Some(start), Some(end)) => Some(end - start),
233 _ => None,
234 }
235 }
236
237 pub fn add_custom(&mut self, key: impl Into<String>, value: impl Into<String>) {
239 self.custom.insert(key.into(), value.into());
240 }
241
242 fn merge(&mut self, other: AnalysisMetadata) {
244 if self.dataset_name.is_none() {
245 self.dataset_name = other.dataset_name;
246 }
247 if self.start_time.is_none() {
248 self.start_time = other.start_time;
249 }
250 if self.end_time.is_none() {
251 self.end_time = other.end_time;
252 }
253 self.custom.extend(other.custom);
254 }
255}
256
257impl Default for AnalysisMetadata {
258 fn default() -> Self {
259 Self::new()
260 }
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
265pub struct AnalysisError {
266 pub analyzer_name: String,
268
269 pub error: String,
271}
272
273#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct AnalysisSummary {
276 pub total_metrics: usize,
278
279 pub total_errors: usize,
281
282 pub analyzer_count: usize,
284
285 pub dataset_name: Option<String>,
287}