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 RepeatedInTransaction { path: String, count: usize },
67 CodeNotAllowedForPid { value: String, allowed: Vec<String> },
69 AnswerNotInList {
74 answer: String,
75 list: String,
76 allowed: Vec<String>,
77 },
78 ValueTooLong {
80 field_name: String,
81 length: usize,
82 max: usize,
83 },
84 ValueNotNumeric { field_name: String },
87 UntSegmentCountMismatch { declared: usize, actual: usize },
89 UntCountNotVerifiable { unh_count: usize },
91 StructureDiagnostic {
98 kind: StructureDiagnosticKind,
99 segment_id: String,
100 position: usize,
101 detail: String,
102 },
103}
104
105impl IssueKind {
106 pub fn code(&self) -> &'static str {
108 match self {
109 Self::MissingRequiredField { .. } => "AHB001",
110 Self::GroupNotAllowed { .. } | Self::SegmentNotAllowed { .. } => "AHB002",
111 Self::FieldConditionNotSatisfied { .. } => "AHB003",
112 Self::ConditionUnknown { .. } => "AHB005",
113 Self::PackageCardinality { .. } => "AHB006",
114 Self::RepeatedInTransaction { .. } => "AHB007",
115 Self::CodeNotAllowedForPid { .. } => "COD002",
116 Self::AnswerNotInList { .. } => "COD003",
117 Self::ValueTooLong { .. } => "FMT001",
118 Self::ValueNotNumeric { .. } => "FMT002",
119 Self::UntSegmentCountMismatch { .. } | Self::UntCountNotVerifiable { .. } => "STR007",
120 Self::StructureDiagnostic { kind, .. } => match kind {
125 StructureDiagnosticKind::MissingRequiredSegment => "STR001",
126 StructureDiagnosticKind::MaxRepetitionsExceeded => "STR002",
127 StructureDiagnosticKind::UnexpectedSegment
128 | StructureDiagnosticKind::UnrecognizedQualifier => "STR003",
129 StructureDiagnosticKind::MissingRequiredGroup => "STR005",
130 StructureDiagnosticKind::GroupMaxRepetitionsExceeded => "STR006",
131 StructureDiagnosticKind::SkippedUnknownSegment
132 | StructureDiagnosticKind::OrphanedGroupSegment => "STR008",
133 },
134 }
135 }
136
137 pub fn category(&self) -> ValidationCategory {
139 match self {
140 Self::MissingRequiredField { .. }
141 | Self::GroupNotAllowed { .. }
142 | Self::SegmentNotAllowed { .. }
143 | Self::FieldConditionNotSatisfied { .. }
144 | Self::ConditionUnknown { .. }
145 | Self::PackageCardinality { .. }
146 | Self::RepeatedInTransaction { .. } => ValidationCategory::Ahb,
147 Self::CodeNotAllowedForPid { .. } | Self::AnswerNotInList { .. } => {
148 ValidationCategory::Code
149 }
150 Self::UntSegmentCountMismatch { .. }
151 | Self::UntCountNotVerifiable { .. }
152 | Self::StructureDiagnostic { .. } => ValidationCategory::Structure,
153 Self::ValueTooLong { .. } | Self::ValueNotNumeric { .. } => ValidationCategory::Format,
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use mig_assembly::StructureDiagnosticKind;
162
163 #[test]
164 fn code_matches_the_legacy_error_code_for_every_kind() {
165 let cases: Vec<(IssueKind, &str)> = vec![
166 (
167 IssueKind::MissingRequiredField {
168 field_name: "f".into(),
169 },
170 "AHB001",
171 ),
172 (
173 IssueKind::GroupNotAllowed {
174 group: "SG4/SG5".into(),
175 },
176 "AHB002",
177 ),
178 (
179 IssueKind::SegmentNotAllowed {
180 segment: "SG4/SG8/SG10/CAV".into(),
181 },
182 "AHB002",
183 ),
184 (
185 IssueKind::FieldConditionNotSatisfied {
186 field_name: "f".into(),
187 },
188 "AHB003",
189 ),
190 (
191 IssueKind::ConditionUnknown {
192 field_name: "f".into(),
193 unresolved: UnresolvedConditions::default(),
194 },
195 "AHB005",
196 ),
197 (
198 IssueKind::PackageCardinality {
199 package_id: "1P0..1".into(),
200 present: 2,
201 min: 0,
202 max: 1,
203 codes: vec![],
204 },
205 "AHB006",
206 ),
207 (
208 IssueKind::CodeNotAllowedForPid {
209 value: "X".into(),
210 allowed: vec![],
211 },
212 "COD002",
213 ),
214 (
215 IssueKind::AnswerNotInList {
216 answer: "ZB5".into(),
217 list: "G_0059".into(),
218 allowed: vec![],
219 },
220 "COD003",
221 ),
222 (
223 IssueKind::ValueTooLong {
224 field_name: "f".into(),
225 length: 600,
226 max: 512,
227 },
228 "FMT001",
229 ),
230 (
231 IssueKind::UntSegmentCountMismatch {
232 declared: 30,
233 actual: 31,
234 },
235 "STR007",
236 ),
237 (IssueKind::UntCountNotVerifiable { unh_count: 2 }, "STR007"),
238 (
239 IssueKind::StructureDiagnostic {
240 kind: StructureDiagnosticKind::SkippedUnknownSegment,
241 segment_id: "CCI".into(),
242 position: 15,
243 detail: "irrelevant to the code".into(),
244 },
245 "STR008",
246 ),
247 (
248 IssueKind::StructureDiagnostic {
249 kind: StructureDiagnosticKind::UnexpectedSegment,
250 segment_id: "CCI".into(),
251 position: 15,
252 detail: "irrelevant to the code".into(),
253 },
254 "STR003",
255 ),
256 ];
257 for (kind, expected) in cases {
258 assert_eq!(kind.code(), expected, "wrong code for {kind:?}");
259 }
260 }
261
262 #[test]
265 fn each_structure_kind_has_its_own_code() {
266 use StructureDiagnosticKind as K;
267 for (kind, expected) in [
268 (K::MissingRequiredSegment, "STR001"),
269 (K::MaxRepetitionsExceeded, "STR002"),
270 (K::UnexpectedSegment, "STR003"),
271 (K::UnrecognizedQualifier, "STR003"),
272 (K::MissingRequiredGroup, "STR005"),
273 (K::GroupMaxRepetitionsExceeded, "STR006"),
274 (K::SkippedUnknownSegment, "STR008"),
275 (K::OrphanedGroupSegment, "STR008"),
276 ] {
277 let issue = IssueKind::StructureDiagnostic {
278 kind,
279 segment_id: "RFF".into(),
280 position: 1,
281 detail: String::new(),
282 };
283 assert_eq!(issue.code(), expected, "{kind:?}");
284 }
285 }
286
287 #[test]
288 fn category_is_derived_from_the_kind() {
289 assert_eq!(
290 IssueKind::MissingRequiredField {
291 field_name: "f".into()
292 }
293 .category(),
294 ValidationCategory::Ahb
295 );
296 assert_eq!(
297 IssueKind::CodeNotAllowedForPid {
298 value: "X".into(),
299 allowed: vec![]
300 }
301 .category(),
302 ValidationCategory::Code
303 );
304 assert_eq!(
305 IssueKind::UntSegmentCountMismatch {
306 declared: 1,
307 actual: 2
308 }
309 .category(),
310 ValidationCategory::Structure
311 );
312 }
313}