Skip to main content

long_screen/engine/
diagnostic.rs

1//! Structured diagnostics: what a pass found, as a code plus numbers, never text. The host formats the message from
2//! its catalogue (the page's `t()`, or [`crate::engine::catalog::format`] natively) under `diag.<code>.*` with these
3//! parameters, so the zh text it persists stays byte-identical to what the page wrote before the pass moved here.
4//! A diagnostic may also name the canvas it concerns, a confidence and a rectangle, and carry a `detail` payload the
5//! journal row stores as it is.
6
7use crate::kernels::geometry::Rect;
8
9/// How serious a diagnostic is; the same three levels the page's `Severity` has.
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum Severity {
12    Info,
13    Warning,
14    Error,
15}
16
17impl Severity {
18    pub fn as_str(self) -> &'static str {
19        match self {
20            Severity::Info => "info",
21            Severity::Warning => "warning",
22            Severity::Error => "error",
23        }
24    }
25}
26
27/// A `detail` payload: numbers, flags, text, objects whose fields keep the order they are listed in, and lists.
28#[derive(Clone, Debug, PartialEq)]
29pub enum Value {
30    Number(f64),
31    Bool(bool),
32    Text(String),
33    Object(Vec<(&'static str, Value)>),
34    List(Vec<Value>),
35}
36
37impl Value {
38    /// `{ x, y }`.
39    pub fn point(x: f64, y: f64) -> Value {
40        Value::Object(vec![("x", Value::Number(x)), ("y", Value::Number(y))])
41    }
42
43    /// `{ x, y, width, height }`.
44    pub fn rect(r: Rect) -> Value {
45        Value::Object(vec![
46            ("x", Value::Number(r.x)),
47            ("y", Value::Number(r.y)),
48            ("width", Value::Number(r.width)),
49            ("height", Value::Number(r.height)),
50        ])
51    }
52}
53
54/// One diagnostic event. `frame`/`time` locate a per-frame finding; `params` are the catalogue's `{{name}}` values,
55/// in the order the message uses them. `canvas`, `confidence`, `region` and `detail` are the row's own fields of
56/// those names, when it has them.
57#[derive(Clone, Debug, PartialEq)]
58pub struct Diagnostic {
59    pub code: &'static str,
60    pub severity: Severity,
61    pub frame: Option<u32>,
62    pub time: Option<f64>,
63    pub params: Vec<(&'static str, f64)>,
64    pub canvas: Option<String>,
65    pub confidence: Option<f64>,
66    pub region: Option<Rect>,
67    pub detail: Option<Value>,
68}
69
70impl Diagnostic {
71    pub fn new(code: &'static str, severity: Severity) -> Diagnostic {
72        Diagnostic {
73            code,
74            severity,
75            frame: None,
76            time: None,
77            params: Vec::new(),
78            canvas: None,
79            confidence: None,
80            region: None,
81            detail: None,
82        }
83    }
84
85    pub fn at(mut self, frame: u32, time: f64) -> Diagnostic {
86        self.frame = Some(frame);
87        self.time = Some(time);
88        self
89    }
90
91    pub fn param(mut self, name: &'static str, value: f64) -> Diagnostic {
92        self.params.push((name, value));
93        self
94    }
95
96    pub fn canvas(mut self, canvas: &str) -> Diagnostic {
97        self.canvas = Some(canvas.to_string());
98        self
99    }
100
101    pub fn confidence(mut self, confidence: f64) -> Diagnostic {
102        self.confidence = Some(confidence);
103        self
104    }
105
106    pub fn region(mut self, region: Rect) -> Diagnostic {
107        self.region = Some(region);
108        self
109    }
110
111    pub fn detail(mut self, detail: Value) -> Diagnostic {
112        self.detail = Some(detail);
113        self
114    }
115}