automapper_validation/eval/
sg4_instances.rs1use mig_types::segment::OwnedSegment;
10
11use super::EvaluationContext;
12
13pub type Path<'a> = Vec<(&'a str, usize)>;
15
16pub fn sg8s<'a>(ctx: &EvaluationContext<'a>) -> Vec<Path<'a>> {
19 ctx.scopes_under("SG4", "SG8").unwrap_or_default()
20}
21
22pub fn sg8s_or_all<'a>(ctx: &EvaluationContext<'a>) -> Vec<Path<'a>> {
25 if let Some(own) = ctx.scopes_under("SG4", "SG8") {
26 return own;
27 }
28 let Some(nav) = ctx.navigator() else {
29 return Vec::new();
30 };
31 (0..nav.group_instance_count(&["SG4"]))
32 .flat_map(|i| {
33 (0..nav.child_instance_count(&[("SG4", i)], "SG8"))
34 .map(move |j| vec![("SG4", i), ("SG8", j)])
35 })
36 .collect()
37}
38
39pub fn seq_with_cci(
43 ctx: &EvaluationContext,
44 seq_codes: &[&str],
45 klassentyp: &str,
46 merkmal: &str,
47) -> bool {
48 sg8s_or_all(ctx).iter().any(|path| {
49 seq_at(ctx, path).is_some_and(|(code, _)| seq_codes.contains(&code.as_str()))
50 && sg10_at(ctx, path, |segs| has_cci(segs, klassentyp, merkmal))
51 })
52}
53
54pub fn seq_at(ctx: &EvaluationContext, path: &[(&str, usize)]) -> Option<(String, String)> {
56 ctx.find_segments_at(path, "SEQ").first().map(|s| {
57 (
58 s.get_element(0).to_string(),
59 s.get_component(1, 0).to_string(),
60 )
61 })
62}
63
64pub fn rffs_at(
66 ctx: &EvaluationContext,
67 path: &[(&str, usize)],
68 qualifier: &str,
69) -> Vec<OwnedSegment> {
70 ctx.find_segments_at(path, "RFF")
71 .into_iter()
72 .filter(|s| s.get_element(0) == qualifier)
73 .collect()
74}
75
76pub fn rff_values_at(
78 ctx: &EvaluationContext,
79 path: &[(&str, usize)],
80 qualifier: &str,
81) -> Vec<String> {
82 rffs_at(ctx, path, qualifier)
83 .iter()
84 .map(|s| s.get_component(0, 1).to_string())
85 .filter(|v| !v.is_empty())
86 .collect()
87}
88
89pub fn sg10_at(
92 ctx: &EvaluationContext,
93 path: &[(&str, usize)],
94 pred: impl Fn(&[OwnedSegment]) -> bool,
95) -> bool {
96 ctx.child_segments_at(path, "SG10", &["CCI", "CAV"])
97 .iter()
98 .any(|segs| pred(segs))
99}
100
101pub fn has_cci(segs: &[OwnedSegment], klassentyp: &str, merkmal: &str) -> bool {
104 segs.iter().any(|s| {
105 s.id == "CCI"
106 && (klassentyp.is_empty() || s.get_element(0) == klassentyp)
107 && s.get_component(2, 0) == merkmal
108 })
109}
110
111pub fn has_cav(segs: &[OwnedSegment], code: &str) -> bool {
113 segs.iter()
114 .any(|s| s.id == "CAV" && s.get_element(0) == code)
115}
116
117pub fn malo_ids(ctx: &EvaluationContext) -> Vec<String> {
119 ctx.scopes_under("SG4", "SG5")
120 .unwrap_or_default()
121 .iter()
122 .flat_map(|path| ctx.find_segments_at(path, "LOC"))
123 .filter(|s| s.get_element(0) == "Z16")
124 .map(|s| s.get_component(1, 0).to_string())
125 .filter(|v| !v.is_empty())
126 .collect()
127}
128
129pub fn z01_of_malo_has(
133 ctx: &EvaluationContext,
134 refs: &[String],
135 pred: impl Fn(&[OwnedSegment]) -> bool,
136) -> bool {
137 let malos = malo_ids(ctx);
138 let refs: Vec<&String> = refs.iter().filter(|r| malos.contains(r)).collect();
139 if refs.is_empty() {
140 return false;
141 }
142 sg8s(ctx).iter().any(|path| {
143 seq_at(ctx, path).is_some_and(|(code, _)| code == "Z01")
144 && rff_values_at(ctx, path, "Z18")
145 .iter()
146 .any(|v| refs.contains(&v))
147 && sg10_at(ctx, path, &pred)
148 })
149}
150
151pub fn other_sg8s<'a>(ctx: &EvaluationContext<'a>, own: &[(&str, usize)]) -> Vec<Path<'a>> {
153 sg8s(ctx)
154 .into_iter()
155 .filter(|path| path.as_slice() != own)
156 .collect()
157}
158
159pub fn z02_of_same_malo_has(
164 ctx: &EvaluationContext,
165 own: &[(&str, usize)],
166 obis: impl Fn(&str) -> bool,
167) -> bool {
168 let refs = rff_values_at(ctx, own, "Z18");
169 !refs.is_empty()
170 && other_sg8s(ctx, own).iter().any(|path| {
171 seq_at(ctx, path).is_some_and(|(code, _)| code == "Z02")
172 && rff_values_at(ctx, path, "Z18")
173 .iter()
174 .any(|v| refs.contains(v))
175 && ctx
176 .find_segments_at(path, "PIA")
177 .iter()
178 .any(|s| s.get_element(0) == "5" && obis(s.get_component(1, 0)))
179 })
180}
181
182pub fn objektcode_elsewhere(
188 ctx: &EvaluationContext,
189 own: &[(&str, usize)],
190 kinds: &[&str],
191 same_nummer: bool,
192 same_zeitraum: bool,
193) -> bool {
194 let Some((code, zeitraum)) = seq_at(ctx, own).filter(|(c, _)| kinds.contains(&c.as_str()))
195 else {
196 return false;
197 };
198 let objektcodes = |path: &[(&str, usize)]| -> Vec<(String, String)> {
199 rffs_at(ctx, path, "Z33")
200 .iter()
201 .map(|s| {
202 (
203 s.get_component(0, 1).to_string(),
204 s.get_component(0, 2).to_string(),
205 )
206 })
207 .filter(|(c, _)| !c.is_empty())
208 .collect()
209 };
210 let mine = objektcodes(own);
211 !mine.is_empty()
212 && other_sg8s(ctx, own).iter().any(|path| {
213 seq_at(ctx, path).is_some_and(|(c, z)| c == code && (!same_zeitraum || z == zeitraum))
214 && objektcodes(path).iter().any(|(c, n)| {
215 mine.iter()
216 .any(|(mc, mn)| c == mc && (!same_nummer || n == mn))
217 })
218 })
219}
220
221pub fn z01_of_zeitraum<'a>(ctx: &EvaluationContext<'a>, own: &[(&str, usize)]) -> Vec<Path<'a>> {
224 let Some((_, zeitraum)) = seq_at(ctx, own).filter(|(_, z)| !z.is_empty()) else {
225 return Vec::new();
226 };
227 sg8s(ctx)
228 .into_iter()
229 .filter(|path| seq_at(ctx, path).is_some_and(|(c, z)| c == "Z01" && z == zeitraum))
230 .collect()
231}
232
233pub fn ruhende_malo_with_zeitraum(ctx: &EvaluationContext, zeitraum: &str) -> bool {
236 ctx.scopes_under("SG4", "SG5")
237 .unwrap_or_default()
238 .iter()
239 .flat_map(|path| ctx.find_segments_at(path, "LOC"))
240 .any(|s| s.get_element(0) == "Z22" && s.get_component(1, 3) == zeitraum)
241}