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