Skip to main content

discovery/
classifier.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5#[serde(rename_all = "lowercase")]
6pub enum Validity {
7    Valid,
8    Invalid,
9}
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub struct Classification {
13    pub standard: String,
14    pub status: Validity,
15    pub summary: Value,
16    pub reason: Option<String>,
17}
18
19type ClassifierFn = fn(body: &str) -> Classification;
20
21pub struct ClassifierRegistry {
22    classifiers: Vec<(String, ClassifierFn)>,
23}
24
25impl ClassifierRegistry {
26    #[must_use]
27    pub fn new() -> Self {
28        Self {
29            classifiers: Vec::new(),
30        }
31    }
32
33    pub fn register(&mut self, standard: &str, f: ClassifierFn) {
34        self.classifiers.push((standard.to_string(), f));
35    }
36
37    #[must_use]
38    pub fn classify(&self, standard: &str, body: &str) -> Option<Classification> {
39        let (_, f) = self.classifiers.iter().find(|(s, _)| s == standard)?;
40        Some(f(body))
41    }
42}
43
44impl Default for ClassifierRegistry {
45    fn default() -> Self {
46        Self::new()
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    fn dummy_classifier(body: &str) -> Classification {
55        match serde_json::from_str::<Value>(body) {
56            Ok(v) => Classification {
57                standard: "test".into(),
58                status: Validity::Valid,
59                summary: v,
60                reason: None,
61            },
62            Err(e) => Classification {
63                standard: "test".into(),
64                status: Validity::Invalid,
65                summary: Value::Null,
66                reason: Some(e.to_string()),
67            },
68        }
69    }
70
71    #[test]
72    fn valid_body() {
73        let mut reg = ClassifierRegistry::new();
74        reg.register("test", dummy_classifier);
75        let c = reg.classify("test", r#"{"key":"val"}"#).unwrap();
76        assert_eq!(c.status, Validity::Valid);
77        assert!(c.reason.is_none());
78        assert_eq!(c.summary["key"], "val");
79    }
80
81    #[test]
82    fn invalid_body_reported() {
83        let mut reg = ClassifierRegistry::new();
84        reg.register("test", dummy_classifier);
85        let c = reg.classify("test", "not json").unwrap();
86        assert_eq!(c.status, Validity::Invalid);
87        assert!(c.reason.is_some());
88    }
89
90    #[test]
91    fn unknown_standard() {
92        let reg = ClassifierRegistry::new();
93        assert!(reg.classify("missing", "").is_none());
94    }
95}