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}