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