Skip to main content

fml_engine/
engine.rs

1//! Running a StructureMap.
2//!
3//! Follows the HL7 reference engine (`StructureMapUtilities.transform` in org.hl7.fhir.core):
4//! groups run their extended group first, each rule runs once per source item that passes its
5//! filters, targets are created or reused (repeating elements get a new value, `share` reuses one),
6//! dependent groups receive variables by position, and a simple copy without a dependent group is
7//! completed by the default group for the source and target types. Beyond the reference engine, a
8//! rule may have several sources: it then runs once per combination of their items.
9
10use std::collections::{HashMap, HashSet};
11use std::fmt;
12use std::future::Future;
13use std::pin::Pin;
14use std::sync::{Arc, Mutex};
15
16use fml_check::definitions::type_url;
17use fml_check::{Definitions, Lookup, Type, Types};
18use fml_model::model::{
19    Dependent, Group, Parameter, ParameterValue, Rule, Source, StructureMap, Target,
20};
21use fml_model::{SOURCE_ELEMENT_EXTENSION, TARGET_ELEMENT_EXTENSION};
22use serde_json::{Value, json};
23
24use crate::expressions::{Expressions, Item, bind_variables, path_start_names};
25use crate::terminology::{ConceptMapResolver, NoConceptMaps, translate};
26use crate::trace::{Phase, Trace, TraceEvent, TraceStatus, TraceVariable};
27use crate::tree::{NodeId, Tree};
28use crate::{datetime, transforms};
29
30type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
31
32/// A failed transform.
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct Error {
35    pub message: String,
36    /// With tracing on, the events recorded up to the failure (status `stopped`).
37    pub trace: Option<Box<Trace>>,
38}
39
40impl Error {
41    fn new(message: impl Into<String>) -> Self {
42        Self {
43            message: message.into(),
44            trace: None,
45        }
46    }
47
48    fn in_rule(rule: &Rule, message: impl fmt::Display) -> Self {
49        match &rule.name {
50            Some(name) => Self::new(format!("rule `{name}`: {message}")),
51            None => Self::new(message.to_string()),
52        }
53    }
54}
55
56impl fmt::Display for Error {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        f.write_str(&self.message)
59    }
60}
61
62impl std::error::Error for Error {}
63
64type Result<T> = std::result::Result<T, Error>;
65
66/// Finds the maps a map imports, by canonical URL or `*` pattern.
67pub trait MapResolver {
68    fn resolve(&self, url: &str) -> Vec<Arc<StructureMap>>;
69}
70
71/// No imported maps.
72#[derive(Debug, Default, Clone, Copy)]
73pub struct NoMaps;
74
75impl MapResolver for NoMaps {
76    fn resolve(&self, _url: &str) -> Vec<Arc<StructureMap>> {
77        Vec::new()
78    }
79}
80
81/// A fixed set of maps, matched by URL; `*` in an import matches any run of characters.
82#[derive(Debug, Default, Clone)]
83pub struct MapSet {
84    maps: Vec<Arc<StructureMap>>,
85}
86
87impl MapSet {
88    pub fn new() -> Self {
89        Self::default()
90    }
91
92    pub fn add(&mut self, map: StructureMap) {
93        self.maps.push(Arc::new(map));
94    }
95}
96
97impl MapResolver for MapSet {
98    fn resolve(&self, url: &str) -> Vec<Arc<StructureMap>> {
99        self.maps
100            .iter()
101            .filter(|map| {
102                map.url
103                    .as_deref()
104                    .is_some_and(|candidate| match url.split_once('*') {
105                        Some((prefix, suffix)) => {
106                            candidate.len() > url.len()
107                                && candidate.starts_with(prefix)
108                                && candidate.ends_with(suffix)
109                        }
110                        None => candidate == url,
111                    })
112            })
113            .cloned()
114            .collect()
115    }
116}
117
118/// The result of a transform.
119#[derive(Debug, Clone, PartialEq)]
120pub struct Transformed {
121    /// The target, as FHIR JSON.
122    pub target: Value,
123    /// Messages from `log (…)` clauses.
124    pub log: Vec<String>,
125    /// Values left out of the target, such as a code a ConceptMap does not know (the reference
126    /// engine leaves those unset too).
127    pub warnings: Vec<String>,
128    /// Resources the map created outside the target (`create('Observation') as obs` with no
129    /// target element) and did not put into it: the `produced` structures of the map.
130    pub produced: Vec<Value>,
131    /// The debug trace, when tracing is on ([`Engine::with_trace`]).
132    pub trace: Option<Trace>,
133}
134
135/// Runs StructureMaps.
136pub struct Engine<'a, X> {
137    definitions: &'a dyn Definitions,
138    expressions: X,
139    maps: &'a dyn MapResolver,
140    concept_maps: &'a dyn ConceptMapResolver,
141    trace: bool,
142    /// Group lookups, which look through every imported map: the maps do not change between
143    /// transforms, so each answer is computed once.
144    groups: Mutex<GroupCache>,
145}
146
147/// A target type with its FHIR type name.
148type TypedTarget = (Type, Option<String>);
149
150/// Cached group lookups, by map URL.
151#[derive(Default)]
152struct GroupCache {
153    by_name: HashMap<(String, String), Option<GroupRef>>,
154    by_types: HashMap<(String, String, String), Option<GroupRef>>,
155    default_target: HashMap<(String, String), Option<TypedTarget>>,
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159enum Mode {
160    Input,
161    Output,
162    Shared,
163}
164
165/// Variables in scope: later entries hide earlier ones.
166#[derive(Debug, Clone, Default)]
167struct Vars {
168    entries: Vec<(Mode, String, NodeId)>,
169}
170
171impl Vars {
172    fn add(&mut self, mode: Mode, name: &str, node: NodeId) {
173        self.entries.push((mode, name.to_owned(), node));
174    }
175
176    fn get(&self, mode: Mode, name: &str) -> Option<NodeId> {
177        self.entries
178            .iter()
179            .rev()
180            .find(|(m, n, _)| *m == mode && n == name)
181            .map(|(_, _, node)| *node)
182    }
183
184    /// An input, or an output used as an input, as the reference engine allows.
185    fn value(&self, name: &str) -> Option<NodeId> {
186        self.get(Mode::Input, name)
187            .or_else(|| self.get(Mode::Output, name))
188    }
189
190    fn names(&self) -> impl Iterator<Item = &str> {
191        self.entries
192            .iter()
193            .filter(|(mode, _, _)| *mode != Mode::Shared)
194            .map(|(_, name, _)| name.as_str())
195    }
196}
197
198/// A group of a map.
199#[derive(Clone)]
200struct GroupRef {
201    map: Arc<StructureMap>,
202    index: usize,
203}
204
205impl GroupRef {
206    fn group(&self) -> &Group {
207        &self.map.group[self.index]
208    }
209}
210
211/// State of one transform.
212#[derive(Default)]
213struct Run {
214    tree: Tree,
215    log: Vec<String>,
216    warnings: Vec<String>,
217    depth: usize,
218    /// Recorded events, when tracing.
219    trace: Option<Vec<TraceEvent>>,
220    trace_depth: usize,
221    /// Resources created outside the target.
222    produced: Vec<NodeId>,
223    /// Evaluated `let` constants, by map URL and name.
224    constants: HashMap<(String, String), Vec<NodeId>>,
225}
226
227const MAX_DEPTH: usize = 512;
228const CORE: &str = "http://hl7.org/fhir/StructureDefinition/";
229
230impl<'a, X: Expressions> Engine<'a, X> {
231    pub fn new(definitions: &'a dyn Definitions, expressions: X) -> Self {
232        Self {
233            definitions,
234            expressions,
235            maps: &NoMaps,
236            concept_maps: &NoConceptMaps,
237            trace: false,
238            groups: Mutex::new(GroupCache::default()),
239        }
240    }
241
242    fn cache(&self) -> std::sync::MutexGuard<'_, GroupCache> {
243        self.groups.lock().unwrap_or_else(|e| e.into_inner())
244    }
245
246    /// Records a debug trace of each transform: see [`Trace`].
247    pub fn with_trace(mut self, trace: bool) -> Self {
248        self.trace = trace;
249        self
250    }
251
252    /// Where `translate` finds ConceptMaps that are not contained in the map.
253    pub fn with_concept_maps(mut self, concept_maps: &'a dyn ConceptMapResolver) -> Self {
254        self.concept_maps = concept_maps;
255        self
256    }
257
258    /// Where the maps named in `imports` come from.
259    pub fn with_maps(mut self, maps: &'a dyn MapResolver) -> Self {
260        self.maps = maps;
261        self
262    }
263
264    fn types(&self) -> Types<'a> {
265        Types::new(self.definitions)
266    }
267
268    /// Transforms `source` with the map's first group; the target is created from the type of
269    /// that group's target parameter.
270    pub async fn transform(&self, map: &StructureMap, source: &Value) -> Result<Transformed> {
271        self.run(map, source, None).await
272    }
273
274    /// Transforms `source` into an existing `target`.
275    pub async fn transform_into(
276        &self,
277        map: &StructureMap,
278        source: &Value,
279        target: &Value,
280    ) -> Result<Transformed> {
281        self.run(map, source, Some(target)).await
282    }
283
284    async fn run(
285        &self,
286        map: &StructureMap,
287        source: &Value,
288        target: Option<&Value>,
289    ) -> Result<Transformed> {
290        let map = Arc::new(map.clone());
291        let group = map
292            .group
293            .first()
294            .ok_or_else(|| Error::new("the map has no groups"))?;
295        let input = |mode: &str| group.input.iter().find(|input| input.mode == mode);
296        let source_input =
297            input("source").ok_or_else(|| Error::new("the first group has no source"))?;
298        let target_input =
299            input("target").ok_or_else(|| Error::new("the first group has no target"))?;
300
301        let mut run = Run {
302            trace: self.trace.then(Vec::new),
303            ..Run::default()
304        };
305        let types = self.types();
306        let source_type = self.input_type(&map, source_input.type_name.as_deref());
307        let source_node = run.tree.load(source, source_type, &types);
308        let target_type = self.input_type(&map, target_input.type_name.as_deref());
309        let target_node = match target {
310            Some(target) => run.tree.load(target, target_type, &types),
311            None => {
312                if matches!(target_type, Type::Unknown) {
313                    return Err(Error::new(format!(
314                        "cannot create the target: type `{}` is not known",
315                        target_input.type_name.as_deref().unwrap_or("?")
316                    )));
317                }
318                run.tree.create(target_type, None)
319            }
320        };
321        let mut vars = Vars::default();
322        vars.add(Mode::Input, &source_input.name, source_node);
323        vars.add(Mode::Output, &target_input.name, target_node);
324        let entry = GroupRef {
325            map: map.clone(),
326            index: 0,
327        };
328        if let Err(mut error) = self.execute_group(&mut run, entry, vars, true).await {
329            error.trace = run.trace.take().map(|events| {
330                Box::new(Trace {
331                    status: TraceStatus::Stopped,
332                    events,
333                })
334            });
335            return Err(error);
336        }
337        let in_target = run.tree.reachable(target_node);
338        let produced = run
339            .produced
340            .iter()
341            .filter(|node| !in_target.contains(node))
342            .map(|&node| run.tree.to_json(node))
343            .collect();
344        Ok(Transformed {
345            target: run.tree.to_json(target_node),
346            log: run.log,
347            warnings: run.warnings,
348            produced,
349            trace: run.trace.map(|events| Trace {
350                status: TraceStatus::Complete,
351                events,
352            }),
353        })
354    }
355
356    /// The type a group parameter or `create` names: a `uses` alias, a URL, or a type name.
357    fn input_type(&self, map: &StructureMap, name: Option<&str>) -> Type {
358        let Some(name) = name else {
359            return Type::Unknown;
360        };
361        self.types().structure(&self.type_name_url(map, name, CORE))
362    }
363
364    fn type_name_url(&self, map: &StructureMap, name: &str, context: &str) -> String {
365        if let Some(structure) = map
366            .structure
367            .iter()
368            .find(|s| s.alias.as_deref() == Some(name))
369        {
370            return structure.url.clone();
371        }
372        type_url(name, context)
373    }
374
375    // ---- Groups -----------------------------------------------------------------------------
376
377    fn execute_group<'r>(
378        &'r self,
379        run: &'r mut Run,
380        group: GroupRef,
381        vars: Vars,
382        at_root: bool,
383    ) -> BoxFuture<'r, Result<()>> {
384        Box::pin(async move {
385            run.depth += 1;
386            if run.depth > MAX_DEPTH {
387                return Err(Error::new(format!(
388                    "groups nest more than {MAX_DEPTH} deep; is a group calling itself without end?"
389                )));
390            }
391            let map = group.map.clone();
392            let definition = group.group();
393            let position = format!("StructureMap.group[{}]", group.index);
394            self.record(run, Phase::Enter, &map, &position, &vars, Vec::new());
395            if let Some(extends) = &definition.extends {
396                let base = self.group_by_name(&map, extends)?;
397                self.execute_group(run, base, vars.clone(), false).await?;
398            }
399            let mut vars = vars;
400            for (index, rule) in definition.rule.iter().enumerate() {
401                let rule_position = format!("{position}.rule[{index}]");
402                self.execute_rule(run, &group, rule, &rule_position, &mut vars, at_root)
403                    .await?;
404            }
405            self.record(run, Phase::Exit, &map, &position, &vars, Vec::new());
406            run.depth -= 1;
407            Ok(())
408        })
409    }
410
411    /// The group `name`, in the map or the maps it imports.
412    fn group_by_name(&self, map: &Arc<StructureMap>, name: &str) -> Result<GroupRef> {
413        let key = (map.url.clone().unwrap_or_default(), name.to_owned());
414        let cached = self.cache().by_name.get(&key).cloned();
415        let found = match cached {
416            Some(found) => found,
417            None => {
418                let found = self.find_group_by_name(map, name);
419                self.cache().by_name.insert(key, found.clone());
420                found
421            }
422        };
423        found.ok_or_else(|| {
424            Error::new(format!(
425                "no group `{name}` in {} or the maps it imports",
426                map.url.as_deref().unwrap_or("the map")
427            ))
428        })
429    }
430
431    fn find_group_by_name(&self, map: &Arc<StructureMap>, name: &str) -> Option<GroupRef> {
432        if let Some(index) = map.group.iter().position(|g| g.name == name) {
433            return Some(GroupRef {
434                map: map.clone(),
435                index,
436            });
437        }
438        for import in &map.import {
439            for imported in self.maps.resolve(import) {
440                if imported.url == map.url {
441                    continue;
442                }
443                if let Some(index) = imported.group.iter().position(|g| g.name == name) {
444                    return Some(GroupRef {
445                        map: imported.clone(),
446                        index,
447                    });
448                }
449            }
450        }
451        None
452    }
453
454    /// The default group for a source and target type: one with a type mode whose parameter
455    /// types match.
456    fn group_by_types(
457        &self,
458        map: &Arc<StructureMap>,
459        source: Option<&str>,
460        target: Option<&str>,
461    ) -> Option<GroupRef> {
462        let (source, target) = (source?, target?);
463        let key = (
464            map.url.clone().unwrap_or_default(),
465            source.to_owned(),
466            target.to_owned(),
467        );
468        if let Some(found) = self.cache().by_types.get(&key) {
469            return found.clone();
470        }
471        let found = self.find_group_by_types(map, source, target);
472        self.cache().by_types.insert(key, found.clone());
473        found
474    }
475
476    fn find_group_by_types(
477        &self,
478        map: &Arc<StructureMap>,
479        source: &str,
480        target: &str,
481    ) -> Option<GroupRef> {
482        let matching = |candidate: &Arc<StructureMap>| {
483            candidate.group.iter().position(|group| {
484                group.type_mode.is_some()
485                    && matches!(group.input.as_slice(), [s, t]
486                        if s.mode == "source" && t.mode == "target"
487                        && self.stated_type_is(candidate, s.type_name.as_deref(), source)
488                        && self.stated_type_is(candidate, t.type_name.as_deref(), target))
489            })
490        };
491        if let Some(index) = matching(map) {
492            return Some(GroupRef {
493                map: map.clone(),
494                index,
495            });
496        }
497        for import in &map.import {
498            for imported in self.maps.resolve(import) {
499                if imported.url == map.url {
500                    continue;
501                }
502                if let Some(index) = matching(&imported) {
503                    return Some(GroupRef {
504                        map: imported,
505                        index,
506                    });
507                }
508            }
509        }
510        None
511    }
512
513    fn stated_type_is(&self, map: &StructureMap, stated: Option<&str>, actual: &str) -> bool {
514        let Some(stated) = stated else { return false };
515        if stated == actual {
516            return true;
517        }
518        let url = self.type_name_url(map, stated, CORE);
519        self.definitions
520            .structure(&url)
521            .is_some_and(|s| s.type_name == actual)
522    }
523
524    // ---- Rules ------------------------------------------------------------------------------
525
526    fn execute_rule<'r>(
527        &'r self,
528        run: &'r mut Run,
529        group: &'r GroupRef,
530        rule: &'r Rule,
531        position: &'r str,
532        vars: &'r mut Vars,
533        at_root: bool,
534    ) -> BoxFuture<'r, Result<()>> {
535        Box::pin(async move {
536            self.record(run, Phase::Enter, &group.map, position, vars, Vec::new());
537            let mut bindings = vec![vars.clone()];
538            for (index, source) in rule.source.iter().enumerate() {
539                let logged = run.log.len();
540                let mut next = Vec::new();
541                for binding in &bindings {
542                    next.extend(
543                        self.process_source(run, group, rule, source, binding)
544                            .await?,
545                    );
546                }
547                bindings = next;
548                if run.trace.is_some() {
549                    let messages = run.log[logged..].to_vec();
550                    let shown = bindings.first().cloned().unwrap_or_else(|| vars.clone());
551                    let step = format!("{position}.source[{index}]");
552                    self.record(run, Phase::Step, &group.map, &step, &shown, messages);
553                }
554            }
555            let source_variable = match rule.source.as_slice() {
556                [single] => single.variable.as_deref(),
557                _ => None,
558            };
559            for mut binding in bindings {
560                for (index, target) in rule.target.iter().enumerate() {
561                    self.process_target(
562                        run,
563                        group,
564                        rule,
565                        target,
566                        &mut binding,
567                        vars,
568                        source_variable,
569                        at_root,
570                    )
571                    .await?;
572                    if run.trace.is_some() {
573                        let step = format!("{position}.target[{index}]");
574                        self.record(run, Phase::Step, &group.map, &step, &binding, Vec::new());
575                    }
576                }
577                if !rule.rule.is_empty() {
578                    for (index, child) in rule.rule.iter().enumerate() {
579                        let child_position = format!("{position}.rule[{index}]");
580                        self.execute_rule(run, group, child, &child_position, &mut binding, false)
581                            .await?;
582                    }
583                } else if !rule.dependent.is_empty() {
584                    for dependent in &rule.dependent {
585                        self.execute_dependency(run, group, rule, dependent, &binding)
586                            .await?;
587                    }
588                } else if let Some((source, target)) = simple_copy(rule) {
589                    self.default_copy(run, group, rule, source, target, &binding)
590                        .await?;
591                }
592            }
593            self.record(run, Phase::Exit, &group.map, position, vars, Vec::new());
594            Ok(())
595        })
596    }
597
598    /// Records a trace event, when tracing.
599    fn record(
600        &self,
601        run: &mut Run,
602        phase: Phase,
603        map: &StructureMap,
604        position: &str,
605        vars: &Vars,
606        messages: Vec<String>,
607    ) {
608        if run.trace.is_none() {
609            return;
610        }
611        if phase == Phase::Exit {
612            run.trace_depth = run.trace_depth.saturating_sub(1);
613        }
614        let mut seen = HashSet::new();
615        let mut variables: Vec<TraceVariable> = vars
616            .entries
617            .iter()
618            .rev()
619            .filter(|(mode, name, _)| *mode != Mode::Shared && seen.insert(name.clone()))
620            .map(|(mode, name, node)| TraceVariable {
621                name: name.clone(),
622                context_type: match mode {
623                    Mode::Input => "input",
624                    _ => "output",
625                }
626                .to_owned(),
627                value: run.tree.to_json(*node),
628                data_type: self.item(run, *node).type_name,
629            })
630            .collect();
631        variables.reverse();
632        let event = TraceEvent {
633            phase,
634            depth: run.trace_depth,
635            source: map.url.clone(),
636            position: position.to_owned(),
637            variables,
638            messages,
639        };
640        if let Some(events) = &mut run.trace {
641            events.push(event);
642        }
643        if phase == Phase::Enter {
644            run.trace_depth += 1;
645        }
646    }
647
648    /// `src.a as s -> tgt.a as t` with nothing else to do: the default group for the two types
649    /// maps one into the other, or the value is copied when the types are the same.
650    async fn default_copy(
651        &self,
652        run: &mut Run,
653        group: &GroupRef,
654        rule: &Rule,
655        source: &str,
656        target: &str,
657        vars: &Vars,
658    ) -> Result<()> {
659        let source = vars
660            .get(Mode::Input, source)
661            .ok_or_else(|| Error::in_rule(rule, format!("no source variable `{source}`")))?;
662        let target = vars
663            .get(Mode::Output, target)
664            .ok_or_else(|| Error::in_rule(rule, format!("no target variable `{target}`")))?;
665        let source_type = run.tree.fhir_type(source);
666        let target_type = run.tree.fhir_type(target);
667        if let Some(default) =
668            self.group_by_types(&group.map, source_type.as_deref(), target_type.as_deref())
669        {
670            let inputs = &default.group().input;
671            let mut call = Vars::default();
672            call.add(Mode::Input, &inputs[0].name, source);
673            call.add(Mode::Output, &inputs[1].name, target);
674            return self.execute_group(run, default, call, false).await;
675        }
676        if source_type == target_type || target_type.is_none() {
677            run.tree.assign(target, source);
678            return Ok(());
679        }
680        Err(Error::in_rule(
681            rule,
682            format!(
683                "no default group maps {} to {}",
684                source_type.as_deref().unwrap_or("?"),
685                target_type.as_deref().unwrap_or("?")
686            ),
687        ))
688    }
689
690    async fn execute_dependency(
691        &self,
692        run: &mut Run,
693        group: &GroupRef,
694        rule: &Rule,
695        dependent: &Dependent,
696        vars: &Vars,
697    ) -> Result<()> {
698        let callee = self.group_by_name(&group.map, &dependent.name)?;
699        let inputs = &callee.group().input;
700        if inputs.len() != dependent.parameter.len() {
701            return Err(Error::in_rule(
702                rule,
703                format!(
704                    "group `{}` takes {} arguments, the call passes {}",
705                    dependent.name,
706                    inputs.len(),
707                    dependent.parameter.len()
708                ),
709            ));
710        }
711        let mut call = Vars::default();
712        for (input, parameter) in inputs.iter().zip(&dependent.parameter) {
713            let name = parameter_name(parameter);
714            let (mode, node) = if input.mode == "source" {
715                (Mode::Input, vars.value(&name))
716            } else {
717                (Mode::Output, vars.get(Mode::Output, &name))
718            };
719            let node = node.ok_or_else(|| {
720                Error::in_rule(
721                    rule,
722                    format!(
723                        "`{}` passes `{name}` as {} `{}`, but it has no value",
724                        dependent.name, input.mode, input.name
725                    ),
726                )
727            })?;
728            call.add(mode, &input.name, node);
729        }
730        self.execute_group(run, callee, call, false).await
731    }
732
733    // ---- Sources ----------------------------------------------------------------------------
734
735    async fn process_source(
736        &self,
737        run: &mut Run,
738        group: &GroupRef,
739        rule: &Rule,
740        source: &Source,
741        vars: &Vars,
742    ) -> Result<Vec<Vars>> {
743        let context = vars.get(Mode::Input, &source.context).ok_or_else(|| {
744            Error::in_rule(
745                rule,
746                format!("unknown source variable `{}`", source.context),
747            )
748        })?;
749        let mut items = vec![context];
750        for element in element_path(
751            source.element.as_deref(),
752            &source.extension,
753            SOURCE_ELEMENT_EXTENSION,
754        ) {
755            items = items
756                .iter()
757                .flat_map(|&item| run.tree.children(item, &element))
758                .collect();
759        }
760        if items.is_empty()
761            && let Some(default) = &source.default_value
762        {
763            items = self.evaluate(run, group, default, None, vars).await?;
764        }
765        if let Some(type_name) = &source.type_name {
766            items.retain(|&item| run.tree.fhir_type(item).as_deref() == Some(type_name.as_str()));
767        }
768        let with_item = |item: NodeId| {
769            let mut bound = vars.clone();
770            if let Some(variable) = &source.variable {
771                bound.add(Mode::Input, variable, item);
772            }
773            bound
774        };
775        if let Some(condition) = &source.condition {
776            let mut kept = Vec::with_capacity(items.len());
777            for item in items {
778                if self
779                    .evaluate_boolean(run, group, condition, Some(item), &with_item(item))
780                    .await?
781                {
782                    kept.push(item);
783                }
784            }
785            items = kept;
786        }
787        if let Some(check) = &source.check {
788            for &item in &items {
789                if !self
790                    .evaluate_boolean(run, group, check, Some(item), &with_item(item))
791                    .await?
792                {
793                    return Err(Error::in_rule(rule, format!("check `{check}` failed")));
794                }
795            }
796        }
797        if let Some(log) = &source.log_message {
798            let mut messages = Vec::new();
799            for &item in &items {
800                let values = self
801                    .evaluate(run, group, log, Some(item), &with_item(item))
802                    .await?;
803                messages.extend(values.into_iter().filter_map(|v| run.tree.text(v)));
804            }
805            if !messages.is_empty() {
806                run.log.push(messages.join(","));
807            }
808        }
809        match source.list_mode.as_deref() {
810            _ if items.is_empty() => {}
811            Some("first") => items.truncate(1),
812            Some("not_first") => {
813                items.remove(0);
814            }
815            Some("last") => items = vec![*items.last().unwrap()],
816            Some("not_last") => {
817                items.pop();
818            }
819            Some("only_one") if items.len() > 1 => {
820                return Err(Error::in_rule(
821                    rule,
822                    format!("`{}` has more than one item", source.context),
823                ));
824            }
825            _ => {}
826        }
827        Ok(items.into_iter().map(with_item).collect())
828    }
829
830    // ---- Targets ----------------------------------------------------------------------------
831
832    #[allow(clippy::too_many_arguments)]
833    async fn process_target(
834        &self,
835        run: &mut Run,
836        group: &GroupRef,
837        rule: &Rule,
838        target: &Target,
839        vars: &mut Vars,
840        shared: &mut Vars,
841        source_variable: Option<&str>,
842        at_root: bool,
843    ) -> Result<()> {
844        let types = self.types();
845        let elements = element_path(
846            target.element.as_deref(),
847            &target.extension,
848            TARGET_ELEMENT_EXTENSION,
849        );
850        let element = elements.last().cloned();
851        let mut destination = match &target.context {
852            Some(context) => Some(vars.get(Mode::Output, context).ok_or_else(|| {
853                Error::in_rule(rule, format!("unknown target variable `{context}`"))
854            })?),
855            None => None,
856        };
857        let sharing = target.list_mode.iter().any(|m| m == "share");
858        let mut value = match (&target.transform, sharing, &target.list_rule_id) {
859            (None, true, Some(id)) => shared.get(Mode::Shared, id),
860            _ => None,
861        };
862        if let Some(dest) = destination
863            && value.is_none()
864        {
865            let mut dest = dest;
866            for intermediate in elements.iter().take(elements.len().saturating_sub(1)) {
867                dest = run
868                    .tree
869                    .make_property(dest, intermediate, &types)
870                    .map_err(|e| Error::in_rule(rule, e))?;
871            }
872            destination = Some(dest);
873        }
874        if target.transform.is_some() {
875            value = self
876                .run_transform(
877                    run,
878                    group,
879                    rule,
880                    target,
881                    vars,
882                    destination,
883                    element.as_deref(),
884                    source_variable,
885                    at_root,
886                )
887                .await?;
888            if let (Some(produced), Some(dest)) = (value, destination) {
889                match &element {
890                    Some(element) => run
891                        .tree
892                        .set_property(dest, element, produced, &types)
893                        .map_err(|e| Error::in_rule(rule, e))?,
894                    None => run.tree.assign(dest, produced),
895                }
896            }
897        } else if let (Some(dest), None) = (destination, value) {
898            value = Some(match (&element, sharing, &target.list_rule_id) {
899                (Some(element), true, Some(id)) => {
900                    let created = run
901                        .tree
902                        .make_property(dest, element, &types)
903                        .map_err(|e| Error::in_rule(rule, e))?;
904                    shared.add(Mode::Shared, id, created);
905                    created
906                }
907                (Some(element), _, _) => run
908                    .tree
909                    .make_property(dest, element, &types)
910                    .map_err(|e| Error::in_rule(rule, e))?,
911                (None, _, _) => dest,
912            });
913        }
914        if let (Some(variable), Some(value)) = (&target.variable, value) {
915            vars.add(Mode::Output, variable, value);
916        }
917        Ok(())
918    }
919
920    #[allow(clippy::too_many_arguments)]
921    async fn run_transform(
922        &self,
923        run: &mut Run,
924        group: &GroupRef,
925        rule: &Rule,
926        target: &Target,
927        vars: &Vars,
928        destination: Option<NodeId>,
929        element: Option<&str>,
930        source_variable: Option<&str>,
931        _at_root: bool,
932    ) -> Result<Option<NodeId>> {
933        let transform = target.transform.as_deref().unwrap_or("copy");
934        let parameters = &target.parameter;
935        let fail = |message: String| Error::in_rule(rule, message);
936        let types = self.types();
937        match transform {
938            "create" => {
939                let ty = match parameters.first() {
940                    Some(parameter) => {
941                        let name = self.parameter_text(run, group, parameter, vars).await?;
942                        let context = destination
943                            .and_then(|d| run.tree.ty(d).structure().map(|s| s.url.clone()))
944                            .unwrap_or_else(|| CORE.to_owned());
945                        let ty = types.structure(&self.type_name_url(&group.map, &name, &context));
946                        (ty, Some(name))
947                    }
948                    None => self
949                        .implied_type(run, group, destination, element, source_variable, vars)
950                        .map_err(fail)?,
951                };
952                let created = run.tree.create(ty.0, ty.1);
953                // A resource created outside any target element is one the map produces.
954                if destination.is_none() && run.tree.ty(created).is_resource() {
955                    run.produced.push(created);
956                }
957                Ok(Some(created))
958            }
959            "copy" => {
960                let parameter = parameters
961                    .first()
962                    .ok_or_else(|| fail("copy needs a value".into()))?;
963                let node = self.parameter_node(run, group, parameter, vars).await?;
964                Ok(Some(run.tree.deep_copy(node)))
965            }
966            "evaluate" => {
967                let expression = match parameters.last().map(|p| &p.value) {
968                    Some(ParameterValue::String(expression)) => expression.clone(),
969                    _ => return Err(fail("evaluate needs an expression".into())),
970                };
971                let focus = if parameters.len() == 2 {
972                    Some(
973                        self.parameter_node(run, group, &parameters[0], vars)
974                            .await?,
975                    )
976                } else {
977                    None
978                };
979                let values = self.evaluate(run, group, &expression, focus, vars).await?;
980                match values.as_slice() {
981                    [] => Ok(None),
982                    [single] => Ok(Some(*single)),
983                    many => Err(fail(format!(
984                        "`{expression}` gave {} values where one was expected",
985                        many.len()
986                    ))),
987                }
988            }
989            "truncate" => {
990                let text = self.argument_text(run, group, parameters, 0, vars).await?;
991                let length = self.argument_text(run, group, parameters, 1, vars).await?;
992                let truncated = match length.parse::<usize>() {
993                    Ok(length) => text.chars().take(length).collect(),
994                    Err(_) => text,
995                };
996                Ok(Some(self.string(run, truncated, "string")))
997            }
998            "cast" => {
999                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1000                let type_name = if parameters.len() > 1 {
1001                    self.argument_text(run, group, parameters, 1, vars).await?
1002                } else {
1003                    // Without a type, the value takes the type of the element it is written to.
1004                    self.implied_type(run, group, destination, element, None, vars)
1005                        .ok()
1006                        .and_then(|(_, name)| name)
1007                        .ok_or_else(|| fail("cast needs a type: cast(value, 'type')".into()))?
1008                };
1009                let value = transforms::cast(&text, &type_name).map_err(fail)?;
1010                let ty = types.structure(&type_url(&type_name, CORE));
1011                Ok(Some(run.tree.primitive(value, ty, Some(type_name))))
1012            }
1013            "append" => {
1014                let mut text = String::new();
1015                for index in 0..parameters.len() {
1016                    text.push_str(
1017                        &self
1018                            .argument_text(run, group, parameters, index, vars)
1019                            .await?,
1020                    );
1021                }
1022                Ok(Some(self.string(run, text, "string")))
1023            }
1024            "translate" => self.translate(run, group, rule, parameters, vars).await,
1025            "reference" | "pointer" => {
1026                let parameter = parameters
1027                    .first()
1028                    .ok_or_else(|| fail(format!("{transform} needs a resource")))?;
1029                let resource = self.parameter_node(run, group, parameter, vars).await?;
1030                let resource_type = run
1031                    .tree
1032                    .fhir_type(resource)
1033                    .filter(|_| {
1034                        run.tree.ty(resource).is_resource() || !run.tree.is_primitive(resource)
1035                    })
1036                    .ok_or_else(|| fail(format!("{transform} needs a resource")))?;
1037                let id = match run
1038                    .tree
1039                    .children(resource, "id")
1040                    .first()
1041                    .and_then(|&id| run.tree.text(id))
1042                {
1043                    Some(id) => id,
1044                    None => {
1045                        let id = uuid::Uuid::new_v4().to_string();
1046                        run.tree
1047                            .set_text(resource, "id", &id, &types)
1048                            .map_err(fail)?;
1049                        id
1050                    }
1051                };
1052                Ok(Some(if transform == "reference" {
1053                    self.string(run, format!("{resource_type}/{id}"), "string")
1054                } else {
1055                    self.string(run, format!("urn:uuid:{id}"), "uri")
1056                }))
1057            }
1058            "uuid" => Ok(Some(self.string(
1059                run,
1060                uuid::Uuid::new_v4().to_string(),
1061                "id",
1062            ))),
1063            "toDateTime" | "toDate" | "toTime" => {
1064                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1065                let format = match parameters.get(1) {
1066                    Some(_) => Some(self.argument_text(run, group, parameters, 1, vars).await?),
1067                    None => None,
1068                };
1069                let (value, type_name) = match transform {
1070                    "toDateTime" => (datetime::to_date_time(&text, format.as_deref()), "dateTime"),
1071                    "toDate" => (datetime::to_date(&text, format.as_deref()), "date"),
1072                    _ => (datetime::to_time(&text, format.as_deref()), "time"),
1073                };
1074                Ok(Some(self.string(run, value.map_err(fail)?, type_name)))
1075            }
1076            "unixToDateTime" | "unixToDate" | "unixToTime" => {
1077                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1078                let seconds: i64 = text
1079                    .trim()
1080                    .parse()
1081                    .map_err(|_| fail(format!("`{text}` is not a Unix timestamp in seconds")))?;
1082                let timezone = match parameters.get(1) {
1083                    Some(_) => Some(self.argument_text(run, group, parameters, 1, vars).await?),
1084                    None => None,
1085                };
1086                let (value, type_name) = match transform {
1087                    "unixToDateTime" => (
1088                        datetime::unix_to_date_time(seconds, timezone.as_deref()),
1089                        "dateTime",
1090                    ),
1091                    "unixToDate" => (datetime::unix_to_date(seconds, timezone.as_deref()), "date"),
1092                    _ => (datetime::unix_to_time(seconds, timezone.as_deref()), "time"),
1093                };
1094                Ok(Some(self.string(run, value.map_err(fail)?, type_name)))
1095            }
1096            "escape" => {
1097                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1098                let (from, to) = match parameters.len() {
1099                    3 => (
1100                        self.argument_text(run, group, parameters, 1, vars).await?,
1101                        self.argument_text(run, group, parameters, 2, vars).await?,
1102                    ),
1103                    _ => (
1104                        "none".to_owned(),
1105                        self.argument_text(run, group, parameters, 1, vars).await?,
1106                    ),
1107                };
1108                let escaped = datetime::escape(&text, &from, &to).map_err(fail)?;
1109                Ok(Some(self.string(run, escaped, "string")))
1110            }
1111            "cc" | "c" | "id" | "cp" | "qty" => {
1112                let mut arguments = Vec::with_capacity(parameters.len());
1113                for index in 0..parameters.len() {
1114                    arguments.push(
1115                        self.argument_text(run, group, parameters, index, vars)
1116                            .await?,
1117                    );
1118                }
1119                let (json, type_name) = match transform {
1120                    "cc" => (transforms::codeable_concept(&arguments), "CodeableConcept"),
1121                    "c" => match arguments.as_slice() {
1122                        [system, code, rest @ ..] => {
1123                            (transforms::coding(system, code, rest.first()), "Coding")
1124                        }
1125                        _ => return Err(fail("c needs a system and a code".into())),
1126                    },
1127                    "id" => (transforms::identifier(&arguments), "Identifier"),
1128                    "cp" => (transforms::contact_point(&arguments), "ContactPoint"),
1129                    _ => (transforms::quantity(&arguments).map_err(fail)?, "Quantity"),
1130                };
1131                let context = destination
1132                    .and_then(|d| run.tree.ty(d).structure().map(|s| s.url.clone()))
1133                    .unwrap_or_else(|| CORE.to_owned());
1134                let ty = types.structure(&type_url(type_name, &context));
1135                let node = run.tree.load(&json, ty, &types);
1136                Ok(Some(node))
1137            }
1138            other => Err(fail(format!("the {other} transform is not supported"))),
1139        }
1140    }
1141
1142    /// The type `create()` makes without a type argument: the element's only type, or the
1143    /// source's type.
1144    fn implied_type(
1145        &self,
1146        run: &Run,
1147        group: &GroupRef,
1148        destination: Option<NodeId>,
1149        element: Option<&str>,
1150        source_variable: Option<&str>,
1151        vars: &Vars,
1152    ) -> std::result::Result<(Type, Option<String>), String> {
1153        let types = self.types();
1154        if let (Some(dest), Some(element)) = (destination, element)
1155            && let Lookup::Found(found) = types.lookup(run.tree.ty(dest), element)
1156            && let [single] = found.element.types.as_slice()
1157            && single != "Resource"
1158            && single != "*"
1159        {
1160            return Ok((types.element_type(&found, None), Some(single.clone())));
1161        }
1162        if let Some(source) = source_variable.and_then(|s| vars.get(Mode::Input, s)) {
1163            // The type the default group for the source's type produces, as the reference engine
1164            // does: a contained R4 Medication becomes the R5 Medication its group maps to.
1165            if let Some(source_type) = run.tree.fhir_type(source)
1166                && let Some(produced) = self.default_target_type(group, &source_type)
1167            {
1168                return Ok(produced);
1169            }
1170            return Ok((run.tree.ty(source).clone(), run.tree.fhir_type(source)));
1171        }
1172        Err("cannot tell what type to create: name it, as in create('Type')".into())
1173    }
1174
1175    /// The target type of the `<<type+>>` group whose source is `source_type`, in the map or the
1176    /// maps it imports.
1177    fn default_target_type(&self, group: &GroupRef, source_type: &str) -> Option<TypedTarget> {
1178        let key = (
1179            group.map.url.clone().unwrap_or_default(),
1180            source_type.to_owned(),
1181        );
1182        if let Some(found) = self.cache().default_target.get(&key) {
1183            return found.clone();
1184        }
1185        let found = self.find_default_target_type(group, source_type);
1186        self.cache().default_target.insert(key, found.clone());
1187        found
1188    }
1189
1190    fn find_default_target_type(&self, group: &GroupRef, source_type: &str) -> Option<TypedTarget> {
1191        let target_of = |map: &Arc<StructureMap>| {
1192            map.group.iter().find_map(|candidate| {
1193                if candidate.type_mode.as_deref() != Some("type-and-types") {
1194                    return None;
1195                }
1196                let [source, target] = candidate.input.as_slice() else {
1197                    return None;
1198                };
1199                if source.mode != "source"
1200                    || target.mode != "target"
1201                    || !self.stated_type_is(map, source.type_name.as_deref(), source_type)
1202                {
1203                    return None;
1204                }
1205                let stated = target.type_name.as_deref()?;
1206                let ty = self
1207                    .types()
1208                    .structure(&self.type_name_url(map, stated, CORE));
1209                let name = ty.fhir_type().unwrap_or_else(|| stated.to_owned());
1210                Some((ty, Some(name)))
1211            })
1212        };
1213        if let Some(found) = target_of(&group.map) {
1214            return Some(found);
1215        }
1216        group.map.import.iter().find_map(|import| {
1217            self.maps
1218                .resolve(import)
1219                .iter()
1220                .filter(|imported| imported.url != group.map.url)
1221                .find_map(target_of)
1222        })
1223    }
1224
1225    /// `translate(value, map, output)`: a code, Coding or CodeableConcept translated with a
1226    /// ConceptMap contained in the map (`'#id'`) or found by the ConceptMap resolver.
1227    async fn translate(
1228        &self,
1229        run: &mut Run,
1230        group: &GroupRef,
1231        rule: &Rule,
1232        parameters: &[Parameter],
1233        vars: &Vars,
1234    ) -> Result<Option<NodeId>> {
1235        let fail = |message: String| Error::in_rule(rule, message);
1236        let source = self
1237            .parameter_node(
1238                run,
1239                group,
1240                parameters
1241                    .first()
1242                    .ok_or_else(|| fail("translate needs a value".into()))?,
1243                vars,
1244            )
1245            .await?;
1246        let map_url = self.argument_text(run, group, parameters, 1, vars).await?;
1247        let output = self.argument_text(run, group, parameters, 2, vars).await?;
1248        let concept_map = match map_url.strip_prefix('#') {
1249            Some(id) => group
1250                .map
1251                .contained
1252                .iter()
1253                .find(|r| r["resourceType"] == "ConceptMap" && r["id"] == id)
1254                .cloned()
1255                .map(Arc::new)
1256                .ok_or_else(|| fail(format!("no contained ConceptMap `{id}`")))?,
1257            None => self
1258                .concept_maps
1259                .resolve(&map_url)
1260                .ok_or_else(|| fail(format!("ConceptMap `{map_url}` not found")))?,
1261        };
1262        let codes = self.codes_of(run, source);
1263        let mut translation = None;
1264        for (system, code) in &codes {
1265            if let Some(found) = translate(&concept_map, system.as_deref(), code)
1266                .map_err(|e| fail(format!("{e} in `{map_url}`")))?
1267            {
1268                translation = Some(found);
1269                break;
1270            }
1271        }
1272        let Some(translation) = translation else {
1273            let shown: Vec<&str> = codes.iter().map(|(_, c)| c.as_str()).collect();
1274            let rule_name = rule.name.as_deref().unwrap_or("?");
1275            run.warnings.push(format!(
1276                "rule `{rule_name}`: `{}` has no translation in `{map_url}`; left out",
1277                shown.join(", ")
1278            ));
1279            return Ok(None);
1280        };
1281
1282        let types = self.types();
1283        let mut coding = serde_json::Map::new();
1284        if let Some(system) = &translation.system {
1285            coding.insert("system".into(), system.clone().into());
1286        }
1287        if let Some(code) = &translation.code {
1288            coding.insert("code".into(), code.clone().into());
1289        }
1290        if let Some(display) = &translation.display {
1291            coding.insert("display".into(), display.clone().into());
1292        }
1293        let text = |value: &Option<String>| value.clone().map_or(Value::Null, Value::String);
1294        let (json, type_name) = match output.as_str() {
1295            "code" => (text(&translation.code), "code"),
1296            "system" => (text(&translation.system), "uri"),
1297            "display" => (text(&translation.display), "string"),
1298            "Coding" => (Value::Object(coding), "Coding"),
1299            "CodeableConcept" => (
1300                json!({ "coding": [Value::Object(coding)] }),
1301                "CodeableConcept",
1302            ),
1303            other => return Err(fail(format!("unknown translate output `{other}`"))),
1304        };
1305        let ty = types.structure(&type_url(type_name, CORE));
1306        Ok(Some(if json.is_object() {
1307            run.tree.load(&json, ty, &types)
1308        } else {
1309            run.tree.primitive(json, ty, Some(type_name.to_owned()))
1310        }))
1311    }
1312
1313    /// The (system, code) pairs a value holds: a code, a Coding, or a CodeableConcept's codings.
1314    fn codes_of(&self, run: &Run, node: NodeId) -> Vec<(Option<String>, String)> {
1315        let text = |id: NodeId, name: &str| {
1316            run.tree
1317                .children(id, name)
1318                .first()
1319                .and_then(|&child| run.tree.text(child))
1320        };
1321        if run.tree.is_primitive(node) {
1322            return run
1323                .tree
1324                .text(node)
1325                .map(|code| (None, code))
1326                .into_iter()
1327                .collect();
1328        }
1329        let codings = run.tree.children(node, "coding");
1330        let codings = if codings.is_empty() {
1331            vec![node]
1332        } else {
1333            codings
1334        };
1335        codings
1336            .into_iter()
1337            .filter_map(|coding| Some((text(coding, "system"), text(coding, "code")?)))
1338            .collect()
1339    }
1340
1341    // ---- Parameters -------------------------------------------------------------------------
1342
1343    /// A parameter as a node: a variable (or constant), or a new primitive for a literal.
1344    async fn parameter_node(
1345        &self,
1346        run: &mut Run,
1347        group: &GroupRef,
1348        parameter: &Parameter,
1349        vars: &Vars,
1350    ) -> Result<NodeId> {
1351        let types = self.types();
1352        let (value, type_name) = match &parameter.value {
1353            ParameterValue::Id(name) => return self.variable(run, group, name, vars).await,
1354            ParameterValue::String(v) => (Value::String(v.clone()), "string"),
1355            ParameterValue::Boolean(v) => (Value::Bool(*v), "boolean"),
1356            ParameterValue::Integer(v) => (Value::from(*v), "integer"),
1357            ParameterValue::Decimal(v) => (Value::Number(v.clone()), "decimal"),
1358            ParameterValue::Date(v) => (Value::String(v.clone()), "date"),
1359            ParameterValue::Time(v) => (Value::String(v.clone()), "time"),
1360            ParameterValue::DateTime(v) => (Value::String(v.clone()), "dateTime"),
1361        };
1362        let ty = types.structure(&type_url(type_name, CORE));
1363        Ok(run.tree.primitive(value, ty, Some(type_name.to_owned())))
1364    }
1365
1366    /// A variable, or failing that a constant of the map.
1367    async fn variable(
1368        &self,
1369        run: &mut Run,
1370        group: &GroupRef,
1371        name: &str,
1372        vars: &Vars,
1373    ) -> Result<NodeId> {
1374        if let Some(node) = vars.value(name) {
1375            return Ok(node);
1376        }
1377        // A dotted id such as `src.code` is a path from a variable.
1378        if let Some((variable, path)) = name.split_once('.')
1379            && let Some(mut node) = vars.value(variable)
1380        {
1381            for segment in path.split('.') {
1382                node = *run
1383                    .tree
1384                    .children(node, segment)
1385                    .first()
1386                    .ok_or_else(|| Error::new(format!("`{name}` has no value")))?;
1387            }
1388            return Ok(node);
1389        }
1390        let values = self.constant(run, group, name).await?;
1391        match values.as_slice() {
1392            [single] => Ok(*single),
1393            [] => Err(Error::new(format!("unknown variable `{name}`"))),
1394            _ => Err(Error::new(format!("constant `{name}` has several values"))),
1395        }
1396    }
1397
1398    async fn parameter_text(
1399        &self,
1400        run: &mut Run,
1401        group: &GroupRef,
1402        parameter: &Parameter,
1403        vars: &Vars,
1404    ) -> Result<String> {
1405        if let ParameterValue::String(text) = &parameter.value {
1406            return Ok(text.clone());
1407        }
1408        let node = self.parameter_node(run, group, parameter, vars).await?;
1409        Ok(run.tree.text(node).unwrap_or_default())
1410    }
1411
1412    async fn argument_text(
1413        &self,
1414        run: &mut Run,
1415        group: &GroupRef,
1416        parameters: &[Parameter],
1417        index: usize,
1418        vars: &Vars,
1419    ) -> Result<String> {
1420        let parameter = parameters
1421            .get(index)
1422            .ok_or_else(|| Error::new(format!("missing argument {}", index + 1)))?;
1423        self.parameter_text(run, group, parameter, vars).await
1424    }
1425
1426    fn string(&self, run: &mut Run, text: String, type_name: &str) -> NodeId {
1427        let ty = self.types().structure(&type_url(type_name, CORE));
1428        run.tree
1429            .primitive(Value::String(text), ty, Some(type_name.to_owned()))
1430    }
1431
1432    // ---- FHIRPath ---------------------------------------------------------------------------
1433
1434    /// The value of the map's `let name = …;`, evaluated once.
1435    fn constant<'r>(
1436        &'r self,
1437        run: &'r mut Run,
1438        group: &'r GroupRef,
1439        name: &'r str,
1440    ) -> BoxFuture<'r, Result<Vec<NodeId>>> {
1441        Box::pin(async move {
1442            let key = (group.map.url.clone().unwrap_or_default(), name.to_owned());
1443            if let Some(values) = run.constants.get(&key) {
1444                return Ok(values.clone());
1445            }
1446            let Some(constant) = group.map.constants.iter().find(|c| c.name == name) else {
1447                return Ok(Vec::new());
1448            };
1449            let values = self
1450                .evaluate(run, group, &constant.value, None, &Vars::default())
1451                .await?;
1452            run.constants.insert(key, values.clone());
1453            Ok(values)
1454        })
1455    }
1456
1457    fn evaluate<'r>(
1458        &'r self,
1459        run: &'r mut Run,
1460        group: &'r GroupRef,
1461        expression: &'r str,
1462        focus: Option<NodeId>,
1463        vars: &'r Vars,
1464    ) -> BoxFuture<'r, Result<Vec<NodeId>>> {
1465        Box::pin(async move {
1466            // Only the variables the expression names: serializing every variable in scope for
1467            // every expression would copy the whole input each time.
1468            let referenced = path_start_names(expression);
1469            let mut names: HashSet<String> = vars
1470                .names()
1471                .filter(|name| referenced.contains(*name))
1472                .map(str::to_owned)
1473                .collect();
1474            let mut variables = Vec::new();
1475            for name in &names {
1476                if let Some(node) = vars.value(name) {
1477                    variables.push((name.clone(), self.item(run, node)));
1478                }
1479            }
1480            for constant in &group.map.constants {
1481                if names.contains(&constant.name) || expression == constant.value {
1482                    continue;
1483                }
1484                if referenced.contains(&constant.name) {
1485                    let values = self.constant(run, group, &constant.name).await?;
1486                    if let [value] = values.as_slice() {
1487                        variables.push((constant.name.clone(), self.item(run, *value)));
1488                        names.insert(constant.name.clone());
1489                    }
1490                }
1491            }
1492            let bound = bind_variables(expression, &names);
1493            let focus = focus.map(|node| self.item(run, node));
1494            let results = self
1495                .expressions
1496                .evaluate(&bound, focus.as_ref(), &variables)
1497                .await
1498                .map_err(|e| Error::new(format!("`{expression}`: {e}")))?;
1499            let types = self.types();
1500            Ok(results
1501                .into_iter()
1502                .map(|item| {
1503                    let ty = item
1504                        .type_name
1505                        .as_deref()
1506                        .map(|t| types.structure(&type_url(t, CORE)))
1507                        .unwrap_or(Type::Unknown);
1508                    if item.json.is_object() {
1509                        run.tree.load(&item.json, ty, &types)
1510                    } else {
1511                        run.tree.primitive(item.json, ty, item.type_name)
1512                    }
1513                })
1514                .collect())
1515        })
1516    }
1517
1518    async fn evaluate_boolean(
1519        &self,
1520        run: &mut Run,
1521        group: &GroupRef,
1522        expression: &str,
1523        focus: Option<NodeId>,
1524        vars: &Vars,
1525    ) -> Result<bool> {
1526        let values = self.evaluate(run, group, expression, focus, vars).await?;
1527        Ok(match values.as_slice() {
1528            [] => false,
1529            [single] => run.tree.value(*single) != Some(&Value::Bool(false)),
1530            _ => true,
1531        })
1532    }
1533
1534    /// A node for FHIRPath. A backbone element is named by its path (`QuestionnaireResponse.item`),
1535    /// as FHIRPath models it, so its elements keep their FHIR types.
1536    fn item(&self, run: &Run, node: NodeId) -> Item {
1537        let type_name = match run.tree.ty(node) {
1538            backbone @ Type::Node { path, .. } if !path.is_empty() => Some(backbone.display()),
1539            _ => run.tree.fhir_type(node),
1540        };
1541        Item {
1542            json: run.tree.to_json(node),
1543            type_name,
1544        }
1545    }
1546}
1547
1548/// The element path of a source or target: the element and the segments in the element
1549/// extension.
1550fn element_path(
1551    element: Option<&str>,
1552    extensions: &[fml_model::model::Extension],
1553    url: &str,
1554) -> Vec<String> {
1555    element
1556        .map(str::to_owned)
1557        .into_iter()
1558        .chain(
1559            extensions
1560                .iter()
1561                .filter(|e| e.url == url)
1562                .filter_map(|e| e.value_string.clone()),
1563        )
1564        .collect()
1565}
1566
1567/// A dependent group argument's variable name.
1568fn parameter_name(parameter: &Parameter) -> String {
1569    match &parameter.value {
1570        ParameterValue::Id(name) | ParameterValue::String(name) => name.clone(),
1571        other => format!("{other:?}"),
1572    }
1573}
1574
1575/// The variables of a rule that copies one value to one target with nothing else to do.
1576fn simple_copy(rule: &Rule) -> Option<(&str, &str)> {
1577    let ([source], [target]) = (rule.source.as_slice(), rule.target.as_slice()) else {
1578        return None;
1579    };
1580    (target.transform.as_deref() == Some("create") && target.parameter.is_empty()).then_some(())?;
1581    Some((source.variable.as_deref()?, target.variable.as_deref()?))
1582}