1use mig_assembly::StructureDiagnosticKind;
7use serde::{Deserialize, Serialize};
8
9use super::issue::ValidationCategory;
10
11#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
15pub struct UnresolvedConditions {
16 pub external: Vec<u32>,
18 pub undetermined: Vec<u32>,
20 pub missing: Vec<u32>,
22}
23
24impl UnresolvedConditions {
25 pub fn is_empty(&self) -> bool {
27 self.external.is_empty() && self.undetermined.is_empty() && self.missing.is_empty()
28 }
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(tag = "type", rename_all = "camelCase")]
38pub enum IssueKind {
39 MissingRequiredField { field_name: String },
41 GroupNotAllowed { group: String },
44 SegmentNotAllowed { segment: String },
48 FieldConditionNotSatisfied { field_name: String },
50 ConditionUnknown {
52 field_name: String,
53 unresolved: UnresolvedConditions,
54 },
55 PackageCardinality {
57 package_id: String,
58 present: usize,
59 min: usize,
60 max: usize,
61 codes: Vec<String>,
62 },
63 CodeNotAllowedForPid { value: String, allowed: Vec<String> },
65 AnswerNotInList {
70 answer: String,
71 list: String,
72 allowed: Vec<String>,
73 },
74 ValueTooLong {
76 field_name: String,
77 length: usize,
78 max: usize,
79 },
80 UntSegmentCountMismatch { declared: usize, actual: usize },
82 UntCountNotVerifiable { unh_count: usize },
84 StructureDiagnostic {
91 kind: StructureDiagnosticKind,
92 segment_id: String,
93 position: usize,
94 detail: String,
95 },
96}
97
98impl IssueKind {
99 pub fn code(&self) -> &'static str {
101 match self {
102 Self::MissingRequiredField { .. } => "AHB001",
103 Self::GroupNotAllowed { .. } | Self::SegmentNotAllowed { .. } => "AHB002",
104 Self::FieldConditionNotSatisfied { .. } => "AHB003",
105 Self::ConditionUnknown { .. } => "AHB005",
106 Self::PackageCardinality { .. } => "AHB006",
107 Self::CodeNotAllowedForPid { .. } => "COD002",
108 Self::AnswerNotInList { .. } => "COD003",
109 Self::ValueTooLong { .. } => "FMT001",
110 Self::UntSegmentCountMismatch { .. } | Self::UntCountNotVerifiable { .. } => "STR007",
111 Self::StructureDiagnostic { kind, .. } => match kind {
116 StructureDiagnosticKind::MissingRequiredSegment => "STR001",
117 StructureDiagnosticKind::MaxRepetitionsExceeded => "STR002",
118 StructureDiagnosticKind::UnexpectedSegment
119 | StructureDiagnosticKind::UnrecognizedQualifier => "STR003",
120 StructureDiagnosticKind::MissingRequiredGroup => "STR005",
121 StructureDiagnosticKind::GroupMaxRepetitionsExceeded => "STR006",
122 StructureDiagnosticKind::SkippedUnknownSegment
123 | StructureDiagnosticKind::OrphanedGroupSegment => "STR008",
124 },
125 }
126 }
127
128 pub fn category(&self) -> ValidationCategory {
130 match self {
131 Self::MissingRequiredField { .. }
132 | Self::GroupNotAllowed { .. }
133 | Self::SegmentNotAllowed { .. }
134 | Self::FieldConditionNotSatisfied { .. }
135 | Self::ConditionUnknown { .. }
136 | Self::PackageCardinality { .. } => ValidationCategory::Ahb,
137 Self::CodeNotAllowedForPid { .. } | Self::AnswerNotInList { .. } => {
138 ValidationCategory::Code
139 }
140 Self::UntSegmentCountMismatch { .. }
141 | Self::UntCountNotVerifiable { .. }
142 | Self::StructureDiagnostic { .. } => ValidationCategory::Structure,
143 Self::ValueTooLong { .. } => ValidationCategory::Format,
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use mig_assembly::StructureDiagnosticKind;
152
153 #[test]
154 fn code_matches_the_legacy_error_code_for_every_kind() {
155 let cases: Vec<(IssueKind, &str)> = vec![
156 (
157 IssueKind::MissingRequiredField {
158 field_name: "f".into(),
159 },
160 "AHB001",
161 ),
162 (
163 IssueKind::GroupNotAllowed {
164 group: "SG4/SG5".into(),
165 },
166 "AHB002",
167 ),
168 (
169 IssueKind::SegmentNotAllowed {
170 segment: "SG4/SG8/SG10/CAV".into(),
171 },
172 "AHB002",
173 ),
174 (
175 IssueKind::FieldConditionNotSatisfied {
176 field_name: "f".into(),
177 },
178 "AHB003",
179 ),
180 (
181 IssueKind::ConditionUnknown {
182 field_name: "f".into(),
183 unresolved: UnresolvedConditions::default(),
184 },
185 "AHB005",
186 ),
187 (
188 IssueKind::PackageCardinality {
189 package_id: "1P0..1".into(),
190 present: 2,
191 min: 0,
192 max: 1,
193 codes: vec![],
194 },
195 "AHB006",
196 ),
197 (
198 IssueKind::CodeNotAllowedForPid {
199 value: "X".into(),
200 allowed: vec![],
201 },
202 "COD002",
203 ),
204 (
205 IssueKind::AnswerNotInList {
206 answer: "ZB5".into(),
207 list: "G_0059".into(),
208 allowed: vec![],
209 },
210 "COD003",
211 ),
212 (
213 IssueKind::ValueTooLong {
214 field_name: "f".into(),
215 length: 600,
216 max: 512,
217 },
218 "FMT001",
219 ),
220 (
221 IssueKind::UntSegmentCountMismatch {
222 declared: 30,
223 actual: 31,
224 },
225 "STR007",
226 ),
227 (IssueKind::UntCountNotVerifiable { unh_count: 2 }, "STR007"),
228 (
229 IssueKind::StructureDiagnostic {
230 kind: StructureDiagnosticKind::SkippedUnknownSegment,
231 segment_id: "CCI".into(),
232 position: 15,
233 detail: "irrelevant to the code".into(),
234 },
235 "STR008",
236 ),
237 (
238 IssueKind::StructureDiagnostic {
239 kind: StructureDiagnosticKind::UnexpectedSegment,
240 segment_id: "CCI".into(),
241 position: 15,
242 detail: "irrelevant to the code".into(),
243 },
244 "STR003",
245 ),
246 ];
247 for (kind, expected) in cases {
248 assert_eq!(kind.code(), expected, "wrong code for {kind:?}");
249 }
250 }
251
252 #[test]
255 fn each_structure_kind_has_its_own_code() {
256 use StructureDiagnosticKind as K;
257 for (kind, expected) in [
258 (K::MissingRequiredSegment, "STR001"),
259 (K::MaxRepetitionsExceeded, "STR002"),
260 (K::UnexpectedSegment, "STR003"),
261 (K::UnrecognizedQualifier, "STR003"),
262 (K::MissingRequiredGroup, "STR005"),
263 (K::GroupMaxRepetitionsExceeded, "STR006"),
264 (K::SkippedUnknownSegment, "STR008"),
265 (K::OrphanedGroupSegment, "STR008"),
266 ] {
267 let issue = IssueKind::StructureDiagnostic {
268 kind,
269 segment_id: "RFF".into(),
270 position: 1,
271 detail: String::new(),
272 };
273 assert_eq!(issue.code(), expected, "{kind:?}");
274 }
275 }
276
277 #[test]
278 fn category_is_derived_from_the_kind() {
279 assert_eq!(
280 IssueKind::MissingRequiredField {
281 field_name: "f".into()
282 }
283 .category(),
284 ValidationCategory::Ahb
285 );
286 assert_eq!(
287 IssueKind::CodeNotAllowedForPid {
288 value: "X".into(),
289 allowed: vec![]
290 }
291 .category(),
292 ValidationCategory::Code
293 );
294 assert_eq!(
295 IssueKind::UntSegmentCountMismatch {
296 declared: 1,
297 actual: 2
298 }
299 .category(),
300 ValidationCategory::Structure
301 );
302 }
303}