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