haystack_core/ontology/
validation.rs1#[derive(Debug, Clone, PartialEq)]
5pub enum FitIssue {
6 MissingMarker {
8 tag: String,
10 spec: String,
12 },
13 WrongType {
15 tag: String,
17 expected: String,
19 actual: String,
21 },
22 InvalidRef {
24 tag: String,
26 expected: String,
28 actual: String,
30 },
31 MissingChoice {
33 tag: String,
35 options: Vec<String>,
37 },
38 ConstraintViolation {
40 tag: String,
42 constraint: String,
44 detail: String,
46 },
47 InvalidChoice {
49 tag: String,
51 value: String,
53 valid_options: Vec<String>,
55 },
56}
57
58#[derive(Debug, Clone, PartialEq)]
60pub struct ValidationIssue {
61 pub entity: Option<String>,
63 pub issue_type: String,
65 pub detail: String,
67}
68
69impl std::fmt::Display for FitIssue {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 match self {
72 FitIssue::MissingMarker { tag, spec } => {
73 write!(f, "missing mandatory marker '{tag}' for spec '{spec}'")
74 }
75 FitIssue::WrongType {
76 tag,
77 expected,
78 actual,
79 } => {
80 write!(
81 f,
82 "wrong type for '{tag}': expected {expected}, got {actual}"
83 )
84 }
85 FitIssue::InvalidRef {
86 tag,
87 expected,
88 actual,
89 } => {
90 write!(
91 f,
92 "invalid ref for '{tag}': expected {expected}, got {actual}"
93 )
94 }
95 FitIssue::MissingChoice { tag, options } => {
96 write!(
97 f,
98 "missing choice for '{tag}': options are {}",
99 options.join(", ")
100 )
101 }
102 FitIssue::ConstraintViolation {
103 tag,
104 constraint,
105 detail,
106 } => {
107 write!(f, "constraint '{constraint}' violated on '{tag}': {detail}")
108 }
109 FitIssue::InvalidChoice {
110 tag,
111 value,
112 valid_options,
113 } => {
114 write!(
115 f,
116 "invalid choice for '{tag}': '{}' not in [{}]",
117 value,
118 valid_options.join(", ")
119 )
120 }
121 }
122 }
123}
124
125impl std::fmt::Display for ValidationIssue {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 write!(
128 f,
129 "[{}] {}: {}",
130 self.issue_type,
131 self.entity.as_deref().unwrap_or("?"),
132 self.detail
133 )
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn fit_issue_missing_marker() {
143 let issue = FitIssue::MissingMarker {
144 tag: "equip".to_string(),
145 spec: "ahu".to_string(),
146 };
147 match &issue {
148 FitIssue::MissingMarker { tag, spec } => {
149 assert_eq!(tag, "equip");
150 assert_eq!(spec, "ahu");
151 }
152 _ => panic!("wrong variant"),
153 }
154 }
155
156 #[test]
157 fn fit_issue_equality() {
158 let a = FitIssue::MissingMarker {
159 tag: "equip".to_string(),
160 spec: "ahu".to_string(),
161 };
162 let b = FitIssue::MissingMarker {
163 tag: "equip".to_string(),
164 spec: "ahu".to_string(),
165 };
166 assert_eq!(a, b);
167 }
168
169 #[test]
170 fn validation_issue() {
171 let issue = ValidationIssue {
172 entity: Some("site-1".to_string()),
173 issue_type: "missing_marker".to_string(),
174 detail: "Entity claims 'ahu' but is missing mandatory marker 'equip'".to_string(),
175 };
176 assert_eq!(issue.entity, Some("site-1".to_string()));
177 assert_eq!(issue.issue_type, "missing_marker");
178 }
179}