1use super::{unhandled_type, Lambda};
6use diagnostics_hierarchy::{ArrayContent, Property as DiagnosticProperty};
7use serde::{Deserialize, Serialize};
8use serde_json::Value as JsonValue;
9
10#[derive(Deserialize, Debug, Clone, Serialize)]
14pub enum MetricValue {
15 Int(i64),
18 Float(f64),
19 String(String),
20 Bool(bool),
21 Vector(Vec<MetricValue>),
22 Bytes(Vec<u8>),
23 Problem(Problem),
24 Lambda(Box<Lambda>),
25 Node,
26}
27
28#[derive(Deserialize, Clone, Serialize)]
30pub enum Problem {
31 Missing(String),
32 UnhandledType(String),
34 SyntaxError(String),
36 ValueError(String),
38 InternalBug(String),
40 Ignore(Vec<Problem>),
42 EvaluationError(String),
44}
45
46impl Problem {
47 pub(crate) fn severity(&self) -> i32 {
51 match self {
52 Problem::Ignore(_) => 1,
53 Problem::Missing(_) => 2,
54 Problem::UnhandledType(_) => 3,
55 Problem::ValueError(_) => 4,
56 Problem::EvaluationError(_) => 5,
57 Problem::SyntaxError(_) => 6,
58 Problem::InternalBug(_) => 7,
59 }
60 }
61}
62
63impl PartialEq for MetricValue {
64 fn eq(&self, other: &Self) -> bool {
65 match (self, other) {
66 (MetricValue::Int(l), MetricValue::Int(r)) => l == r,
67 (MetricValue::Float(l), MetricValue::Float(r)) => l == r,
68 (MetricValue::Bytes(l), MetricValue::Bytes(r)) => l == r,
69 (MetricValue::Int(l), MetricValue::Float(r)) => *l as f64 == *r,
70 (MetricValue::Float(l), MetricValue::Int(r)) => *l == *r as f64,
71 (MetricValue::String(l), MetricValue::String(r)) => l == r,
72 (MetricValue::Bool(l), MetricValue::Bool(r)) => l == r,
73 (MetricValue::Vector(l), MetricValue::Vector(r)) => l == r,
74 _ => false,
75 }
76 }
77}
78
79impl Eq for MetricValue {}
80
81impl std::fmt::Display for MetricValue {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 match self {
84 MetricValue::Int(n) => write!(f, "Int({})", n),
85 MetricValue::Float(n) => write!(f, "Float({})", n),
86 MetricValue::Bool(n) => write!(f, "Bool({})", n),
87 MetricValue::String(n) => write!(f, "String({})", n),
88 MetricValue::Vector(n) => write!(f, "Vector({:?})", n),
89 MetricValue::Bytes(n) => write!(f, "Bytes({:?})", n),
90 MetricValue::Problem(p) => write!(f, "{:?}", p),
91 MetricValue::Lambda(n) => write!(f, "Fn({:?})", n),
92 MetricValue::Node => write!(f, "Node"),
93 }
94 }
95}
96
97impl From<f64> for MetricValue {
98 fn from(val: f64) -> MetricValue {
99 MetricValue::Float(val)
100 }
101}
102
103impl From<i64> for MetricValue {
104 fn from(val: i64) -> MetricValue {
105 MetricValue::Int(val)
106 }
107}
108
109impl From<DiagnosticProperty> for MetricValue {
110 fn from(property: DiagnosticProperty) -> Self {
111 match property {
112 DiagnosticProperty::String(_name, value) => Self::String(value),
113 DiagnosticProperty::Bytes(_name, value) => Self::Bytes(value),
114 DiagnosticProperty::Int(_name, value) => Self::Int(value),
115 DiagnosticProperty::Uint(_name, value) => Self::Int(value as i64),
116 DiagnosticProperty::Double(_name, value) => Self::Float(value),
117 DiagnosticProperty::Bool(_name, value) => Self::Bool(value),
118 DiagnosticProperty::DoubleArray(_name, ArrayContent::Values(values)) => {
120 Self::Vector(values.iter().map(|value| Self::Float(*value)).collect())
121 }
122 DiagnosticProperty::IntArray(_name, ArrayContent::Values(values)) => {
123 Self::Vector(values.iter().map(|value| Self::Int(*value)).collect())
124 }
125 DiagnosticProperty::UintArray(_name, ArrayContent::Values(values)) => {
126 Self::Vector(values.iter().map(|value| Self::Int(*value as i64)).collect())
127 }
128 DiagnosticProperty::DoubleArray(_name, ArrayContent::LinearHistogram(_))
129 | DiagnosticProperty::IntArray(_name, ArrayContent::LinearHistogram(_))
130 | DiagnosticProperty::UintArray(_name, ArrayContent::LinearHistogram(_))
131 | DiagnosticProperty::DoubleArray(_name, ArrayContent::ExponentialHistogram(_))
132 | DiagnosticProperty::IntArray(_name, ArrayContent::ExponentialHistogram(_))
133 | DiagnosticProperty::UintArray(_name, ArrayContent::ExponentialHistogram(_)) => {
134 unhandled_type("Histogram is not supported")
135 }
136 DiagnosticProperty::StringList(_name, _list) => {
137 unhandled_type("StringList is not supported")
138 }
139 }
140 }
141}
142
143impl From<JsonValue> for MetricValue {
144 fn from(value: JsonValue) -> Self {
145 match value {
146 JsonValue::String(value) => Self::String(value),
147 JsonValue::Bool(value) => Self::Bool(value),
148 JsonValue::Number(_) => Self::from(&value),
149 JsonValue::Array(values) => Self::Vector(values.into_iter().map(Self::from).collect()),
150 _ => unhandled_type("Unsupported JSON type"),
151 }
152 }
153}
154
155impl From<&JsonValue> for MetricValue {
156 fn from(value: &JsonValue) -> Self {
157 match value {
158 JsonValue::String(value) => Self::String(value.clone()),
159 JsonValue::Bool(value) => Self::Bool(*value),
160 JsonValue::Number(value) => {
161 if value.is_i64() {
162 Self::Int(value.as_i64().unwrap())
163 } else if value.is_u64() {
164 Self::Int(value.as_u64().unwrap() as i64)
165 } else if value.is_f64() {
166 Self::Float(value.as_f64().unwrap())
167 } else {
168 unhandled_type("Unable to convert JSON number")
169 }
170 }
171 JsonValue::Array(values) => Self::Vector(values.iter().map(Self::from).collect()),
172 _ => unhandled_type("Unsupported JSON type"),
173 }
174 }
175}
176
177impl std::fmt::Debug for Problem {
178 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179 match self {
180 Problem::Missing(s) => write!(f, "Missing: {}", s),
181 Problem::Ignore(problems) => {
182 if problems.len() == 1 {
183 write!(f, "Ignore: {:?}", problems[0])
184 } else {
185 write!(f, "Ignore: [")?;
186 for problem in problems.iter() {
187 write!(f, "{:?}; ", problem)?;
188 }
189 write!(f, "]")
190 }
191 }
192 Problem::SyntaxError(s) => write!(f, "SyntaxError: {}", s),
193 Problem::ValueError(s) => write!(f, "ValueError: {}", s),
194 Problem::InternalBug(s) => write!(f, "InternalBug: {}", s),
195 Problem::UnhandledType(s) => write!(f, "UnhandledType: {}", s),
196 Problem::EvaluationError(s) => write!(f, "EvaluationError: {}", s),
197 }
198 }
199}
200
201#[cfg(test)]
202pub(crate) mod test {
203 use super::*;
204 use crate::assert_problem;
205 use diagnostics_hierarchy::ArrayFormat;
206 use serde_json::{json, Number as JsonNumber};
207
208 #[fuchsia::test]
209 fn test_equality() {
210 assert_eq!(MetricValue::Int(1), MetricValue::Int(1));
212 assert_eq!(MetricValue::Float(1.0), MetricValue::Float(1.0));
213 assert_eq!(MetricValue::String("A".to_string()), MetricValue::String("A".to_string()));
214 assert_eq!(MetricValue::Bool(true), MetricValue::Bool(true));
215 assert_eq!(MetricValue::Bool(false), MetricValue::Bool(false));
216 assert_eq!(
217 MetricValue::Vector(vec![
218 MetricValue::Int(1),
219 MetricValue::Float(1.0),
220 MetricValue::String("A".to_string()),
221 MetricValue::Bool(true),
222 ]),
223 MetricValue::Vector(vec![
224 MetricValue::Int(1),
225 MetricValue::Float(1.0),
226 MetricValue::String("A".to_string()),
227 MetricValue::Bool(true),
228 ])
229 );
230 assert_eq!(MetricValue::Bytes(vec![1, 2, 3]), MetricValue::Bytes(vec![1, 2, 3]));
231
232 assert_eq!(MetricValue::Int(1), MetricValue::Float(1.0));
234 assert_eq!(MetricValue::Float(1.0), MetricValue::Int(1));
235
236 assert!(MetricValue::Int(1) != MetricValue::Vector(vec![MetricValue::Int(1)]));
239 assert!(MetricValue::Bytes(vec![1]) != MetricValue::Vector(vec![MetricValue::Int(1)]));
240 assert!(MetricValue::Int(1) != MetricValue::Bytes(vec![1]));
241
242 assert_eq!(
244 MetricValue::Vector(vec![
245 MetricValue::Int(1),
246 MetricValue::Float(1.0),
247 MetricValue::String("A".to_string()),
248 MetricValue::Bool(true),
249 ]),
250 MetricValue::Vector(vec![
251 MetricValue::Int(1),
252 MetricValue::Float(1.0),
253 MetricValue::String("A".to_string()),
254 MetricValue::Bool(true),
255 ])
256 );
257
258 assert!(
260 MetricValue::Problem(Problem::Missing("err".to_string()))
261 != MetricValue::Problem(Problem::Missing("err".to_string()))
262 );
263 assert_problem!(MetricValue::Problem(Problem::Missing("err".to_string())), "Missing: err");
265
266 }
268
269 #[fuchsia::test]
270 fn test_inequality() {
271 assert_ne!(MetricValue::Int(1), MetricValue::Int(2));
273 assert_ne!(MetricValue::Float(1.0), MetricValue::Float(2.0));
274 assert_ne!(MetricValue::String("A".to_string()), MetricValue::String("B".to_string()));
275 assert_ne!(MetricValue::Bool(true), MetricValue::Bool(false));
276 assert_ne!(
277 MetricValue::Vector(vec![
278 MetricValue::Int(1),
279 MetricValue::Float(1.0),
280 MetricValue::String("A".to_string()),
281 MetricValue::Bool(true),
282 ]),
283 MetricValue::Vector(vec![
284 MetricValue::Int(2),
285 MetricValue::Float(2.0),
286 MetricValue::String("B".to_string()),
287 MetricValue::Bool(false),
288 ])
289 );
290
291 assert_ne!(MetricValue::Int(2), MetricValue::Float(1.0));
293 assert_ne!(MetricValue::Int(1), MetricValue::String("A".to_string()));
294 assert_ne!(MetricValue::Int(1), MetricValue::Bool(true));
295 assert_ne!(MetricValue::Float(1.0), MetricValue::String("A".to_string()));
296 assert_ne!(MetricValue::Float(1.0), MetricValue::Bool(true));
297 assert_ne!(MetricValue::String("A".to_string()), MetricValue::Bool(true));
298 }
299
300 #[fuchsia::test]
301 fn test_fmt() {
302 assert_eq!(format!("{}", MetricValue::Int(3)), "Int(3)");
303 assert_eq!(format!("{}", MetricValue::Float(3.5)), "Float(3.5)");
304 assert_eq!(format!("{}", MetricValue::Bool(true)), "Bool(true)");
305 assert_eq!(format!("{}", MetricValue::Bool(false)), "Bool(false)");
306 assert_eq!(format!("{}", MetricValue::String("cat".to_string())), "String(cat)");
307 assert_eq!(
308 format!("{}", MetricValue::Vector(vec![MetricValue::Int(1), MetricValue::Float(2.5)])),
309 "Vector([Int(1), Float(2.5)])"
310 );
311 assert_eq!(format!("{}", MetricValue::Bytes(vec![1u8, 2u8])), "Bytes([1, 2])");
312 assert_eq!(
313 format!("{}", MetricValue::Problem(Problem::Missing("Where is Foo?".to_string()))),
314 "Missing: Where is Foo?"
315 );
316 assert_eq!(
317 format!("{}", MetricValue::Problem(Problem::ValueError("Where is Foo?".to_string()))),
318 "ValueError: Where is Foo?"
319 );
320 assert_eq!(
321 format!("{}", MetricValue::Problem(Problem::SyntaxError("Where is Foo?".to_string()))),
322 "SyntaxError: Where is Foo?"
323 );
324 assert_eq!(
325 format!("{}", MetricValue::Problem(Problem::InternalBug("Where is Foo?".to_string()))),
326 "InternalBug: Where is Foo?"
327 );
328 assert_eq!(
329 format!(
330 "{}",
331 MetricValue::Problem(Problem::UnhandledType("Where is Foo?".to_string()))
332 ),
333 "UnhandledType: Where is Foo?"
334 );
335 assert_eq!(
336 format!(
337 "{}",
338 MetricValue::Problem(Problem::Ignore(vec![Problem::SyntaxError(
339 "Where is Foo?".to_string()
340 ),]))
341 ),
342 "Ignore: SyntaxError: Where is Foo?"
343 );
344 assert_eq!(
345 format!(
346 "{}",
347 MetricValue::Problem(Problem::Ignore(vec![
348 Problem::SyntaxError("Where is Foo?".to_string()),
349 Problem::ValueError("Where is Bar?".to_string()),
350 ]))
351 ),
352 "Ignore: [SyntaxError: Where is Foo?; ValueError: Where is Bar?; ]"
353 );
354 }
355
356 #[fuchsia::test]
357 fn metric_value_from_json() {
358 macro_rules! test_from {
367 ($json:path, $metric:path, $value:expr) => {
368 test_from_to!($json, $metric, $value, $value);
369 };
370 }
371 macro_rules! test_from_int {
372 ($json:path, $metric:path, $value:expr) => {
373 test_from_to!($json, $metric, JsonNumber::from($value), $value);
374 };
375 }
376 macro_rules! test_from_float {
377 ($json:path, $metric:path, $value:expr) => {
378 test_from_to!($json, $metric, JsonNumber::from_f64($value).unwrap(), $value);
379 };
380 }
381 macro_rules! test_from_to {
382 ($json:path, $metric:path, $json_value:expr, $metric_value:expr) => {
383 let metric_value = $metric($metric_value);
384 let json_value = $json($json_value);
385 assert_eq!(metric_value, MetricValue::from(json_value));
386 };
387 }
388 test_from!(JsonValue::String, MetricValue::String, "Hi World".to_string());
389 test_from_int!(JsonValue::Number, MetricValue::Int, 3);
390 test_from_int!(JsonValue::Number, MetricValue::Int, i64::MAX);
391 test_from_int!(JsonValue::Number, MetricValue::Int, i64::MIN);
392 test_from_to!(JsonValue::Number, MetricValue::Int, JsonNumber::from(u64::MAX), -1);
393 test_from_float!(JsonValue::Number, MetricValue::Float, std::f64::consts::PI);
394 test_from_float!(JsonValue::Number, MetricValue::Float, f64::MAX);
395 test_from_float!(JsonValue::Number, MetricValue::Float, f64::MIN);
396 test_from!(JsonValue::Bool, MetricValue::Bool, true);
397 test_from!(JsonValue::Bool, MetricValue::Bool, false);
398 let json_vec = vec![json!(1), json!(2), json!(3)];
399 let metric_vec = vec![MetricValue::Int(1), MetricValue::Int(2), MetricValue::Int(3)];
400 test_from_to!(JsonValue::Array, MetricValue::Vector, json_vec, metric_vec);
401 assert_problem!(
402 MetricValue::from(JsonValue::Object(serde_json::Map::new())),
403 "UnhandledType: Unsupported JSON type"
404 );
405 }
406
407 #[fuchsia::test]
408 fn metric_value_from_diagnostic_property() {
409 macro_rules! test_from {
428 ($diagnostic:path, $metric:path, $value:expr) => {
429 test_from_to!($diagnostic, $metric, $value, $value);
430 };
431 }
432 macro_rules! test_from_to {
433 ($diagnostic:path, $metric:path, $diagnostic_value:expr, $metric_value:expr) => {
434 assert_eq!(
435 $metric($metric_value),
436 MetricValue::from($diagnostic("foo".to_string(), $diagnostic_value))
437 );
438 };
439 }
440 test_from!(DiagnosticProperty::String, MetricValue::String, "Hi World".to_string());
441 test_from!(DiagnosticProperty::Bytes, MetricValue::Bytes, vec![1, 2, 3]);
442 test_from!(DiagnosticProperty::Int, MetricValue::Int, 3);
443 test_from!(DiagnosticProperty::Int, MetricValue::Int, i64::MAX);
444 test_from!(DiagnosticProperty::Int, MetricValue::Int, i64::MIN);
445 test_from!(DiagnosticProperty::Uint, MetricValue::Int, 3);
446 test_from_to!(DiagnosticProperty::Uint, MetricValue::Int, u64::MAX, -1);
447 test_from!(DiagnosticProperty::Double, MetricValue::Float, std::f64::consts::PI);
448 test_from!(DiagnosticProperty::Double, MetricValue::Float, f64::MAX);
449 test_from!(DiagnosticProperty::Double, MetricValue::Float, f64::MIN);
450 test_from!(DiagnosticProperty::Bool, MetricValue::Bool, true);
451 test_from!(DiagnosticProperty::Bool, MetricValue::Bool, false);
452 let diagnostic_array = ArrayContent::Values(vec![1.5, 2.5, 3.5]);
453 test_from_to!(
454 DiagnosticProperty::DoubleArray,
455 MetricValue::Vector,
456 diagnostic_array,
457 vec![MetricValue::Float(1.5), MetricValue::Float(2.5), MetricValue::Float(3.5)]
458 );
459 let diagnostic_array = ArrayContent::Values(vec![1, 2, 3]);
460 test_from_to!(
461 DiagnosticProperty::IntArray,
462 MetricValue::Vector,
463 diagnostic_array,
464 vec![MetricValue::Int(1), MetricValue::Int(2), MetricValue::Int(3)]
465 );
466 let diagnostic_array = ArrayContent::Values(vec![1, 2, 3]);
467 test_from_to!(
468 DiagnosticProperty::UintArray,
469 MetricValue::Vector,
470 diagnostic_array,
471 vec![MetricValue::Int(1), MetricValue::Int(2), MetricValue::Int(3)]
472 );
473
474 let diagnostic_array = ArrayContent::new(vec![0, 1, 0, 0, 0], ArrayFormat::LinearHistogram)
475 .expect("create histogram");
476 assert_problem!(
477 DiagnosticProperty::UintArray("foo".to_string(), diagnostic_array).into(),
478 "UnhandledType: Histogram is not supported"
479 );
480
481 let diagnostic_array =
482 ArrayContent::new(vec![-10, 1, 0, 0, 0], ArrayFormat::LinearHistogram)
483 .expect("create histogram");
484 assert_problem!(
485 DiagnosticProperty::IntArray("foo".to_string(), diagnostic_array).into(),
486 "UnhandledType: Histogram is not supported"
487 );
488
489 let diagnostic_array =
490 ArrayContent::new(vec![0., 0.1, 0., 0., 0.], ArrayFormat::LinearHistogram)
491 .expect("create histogram");
492 assert_problem!(
493 DiagnosticProperty::DoubleArray("foo".to_string(), diagnostic_array).into(),
494 "UnhandledType: Histogram is not supported"
495 );
496 }
497}