term_guard/analyzers/
types.rs1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::fmt;
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12#[serde(tag = "type", content = "value")]
13pub enum MetricValue {
14 Double(f64),
16
17 Long(i64),
19
20 Histogram(MetricDistribution),
22
23 Vector(Vec<f64>),
25
26 String(String),
28
29 Boolean(bool),
31
32 Map(HashMap<String, MetricValue>),
34}
35
36impl MetricValue {
37 pub fn is_numeric(&self) -> bool {
39 matches!(self, MetricValue::Double(_) | MetricValue::Long(_))
40 }
41
42 pub fn as_f64(&self) -> Option<f64> {
44 match self {
45 MetricValue::Double(v) => Some(*v),
46 MetricValue::Long(v) => Some(*v as f64),
47 _ => None,
48 }
49 }
50
51 pub fn as_i64(&self) -> Option<i64> {
53 match self {
54 MetricValue::Long(v) => Some(*v),
55 MetricValue::Double(v) => {
56 if v.fract() == 0.0 {
57 Some(*v as i64)
58 } else {
59 None
60 }
61 }
62 _ => None,
63 }
64 }
65
66 pub fn to_string_pretty(&self) -> String {
68 match self {
69 MetricValue::Double(v) => {
70 if v.fract() == 0.0 {
71 format!("{v:.0}")
72 } else {
73 format!("{v:.4}")
74 }
75 }
76 MetricValue::Long(v) => v.to_string(),
77 MetricValue::String(s) => s.clone(),
78 MetricValue::Boolean(b) => b.to_string(),
79 MetricValue::Histogram(h) => format!("Histogram({} buckets)", h.buckets.len()),
80 MetricValue::Vector(v) => format!("Vector({} elements)", v.len()),
81 MetricValue::Map(m) => format!("Map({} entries)", m.len()),
82 }
83 }
84}
85
86impl fmt::Display for MetricValue {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 write!(f, "{}", self.to_string_pretty())
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub struct MetricDistribution {
97 pub buckets: Vec<HistogramBucket>,
99
100 pub total_count: u64,
102
103 pub min: Option<f64>,
105
106 pub max: Option<f64>,
108
109 pub mean: Option<f64>,
111
112 pub std_dev: Option<f64>,
114}
115
116impl MetricDistribution {
117 pub fn new() -> Self {
119 Self {
120 buckets: Vec::new(),
121 total_count: 0,
122 min: None,
123 max: None,
124 mean: None,
125 std_dev: None,
126 }
127 }
128
129 pub fn from_buckets(buckets: Vec<HistogramBucket>) -> Self {
131 let total_count = buckets.iter().map(|b| b.count).sum();
132 Self {
133 buckets,
134 total_count,
135 min: None,
136 max: None,
137 mean: None,
138 std_dev: None,
139 }
140 }
141
142 pub fn with_stats(mut self, min: f64, max: f64, mean: f64, std_dev: f64) -> Self {
144 self.min = Some(min);
145 self.max = Some(max);
146 self.mean = Some(mean);
147 self.std_dev = Some(std_dev);
148 self
149 }
150}
151
152impl Default for MetricDistribution {
153 fn default() -> Self {
154 Self::new()
155 }
156}
157
158#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
160pub struct HistogramBucket {
161 pub lower_bound: f64,
163
164 pub upper_bound: f64,
166
167 pub count: u64,
169}
170
171impl HistogramBucket {
172 pub fn new(lower_bound: f64, upper_bound: f64, count: u64) -> Self {
174 Self {
175 lower_bound,
176 upper_bound,
177 count,
178 }
179 }
180
181 pub fn width(&self) -> f64 {
183 self.upper_bound - self.lower_bound
184 }
185
186 pub fn midpoint(&self) -> f64 {
188 (self.lower_bound + self.upper_bound) / 2.0
189 }
190}
191
192pub trait AnalyzerMetric: Into<MetricValue> + Send + Sync + fmt::Debug {}
197
198impl AnalyzerMetric for MetricValue {}
200
201impl From<f64> for MetricValue {
203 fn from(value: f64) -> Self {
204 MetricValue::Double(value)
205 }
206}
207
208impl From<i64> for MetricValue {
210 fn from(value: i64) -> Self {
211 MetricValue::Long(value)
212 }
213}
214
215impl From<bool> for MetricValue {
217 fn from(value: bool) -> Self {
218 MetricValue::Boolean(value)
219 }
220}
221
222impl From<String> for MetricValue {
224 fn from(value: String) -> Self {
225 MetricValue::String(value)
226 }
227}
228
229impl From<&str> for MetricValue {
231 fn from(value: &str) -> Self {
232 MetricValue::String(value.to_string())
233 }
234}