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