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