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.
8//!
9//! Where the Mapping Language IG says more than the reference engine does, the engine follows the
10//! IG: a rule may have several sources and then runs once per combination of their items; named
11//! groups and nested rules after `then` both run, the groups first; a source's `min..max` is
12//! enforced; `evaluate` with several values makes one target per value in a repeating element; a
13//! group name found in several imported maps is an error; `cast` takes `FHIR.` and `System.`
14//! types and checks the value.
15
16use std::collections::{HashMap, HashSet};
17use std::fmt;
18use std::future::Future;
19use std::pin::Pin;
20use std::sync::atomic::{AtomicBool, Ordering};
21use std::sync::{Arc, Mutex};
22
23use fml_check::definitions::type_url;
24use fml_check::{Definitions, Kind, Lookup, Type, Types};
25use fml_model::model::{
26    Dependent, Group, Parameter, ParameterValue, Rule, Source, StructureMap, Target,
27};
28use fml_model::{SOURCE_ELEMENT_EXTENSION, TARGET_ELEMENT_EXTENSION};
29use serde_json::{Value, json};
30
31use crate::expressions::{Expressions, Item, bind_variables, path_start_names};
32use crate::ids::{IdGenerator, RandomIds};
33use crate::terminology::{ConceptMapResolver, NoConceptMaps, TranslateService, translate};
34use crate::trace::{Phase, Trace, TraceEvent, TraceStatus, TraceVariable};
35use crate::tree::{NodeId, Tree};
36use crate::{datetime, transforms};
37
38/// Why `dateOp` cannot run; the checker warns about it too.
39pub(crate) const DATE_OP_UNDEFINED: &str = "dateOp is listed among the StructureMap transforms, but neither the Mapping Language IG nor the transform code system defines its parameters or what it does (\"Parameters to be documented\"); use toDate, toDateTime, toTime or a FHIRPath expression instead";
40
41type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
42
43/// A failed transform.
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct Error {
46    pub kind: ErrorKind,
47    pub message: String,
48    /// The map the failure is in: its canonical URL.
49    pub map: Option<String>,
50    /// Where in that map, as a FHIRPath location: `StructureMap.group[0].rule[2].source[0]`.
51    pub position: Option<String>,
52    /// With tracing on, the events recorded up to the failure (status `stopped`).
53    pub trace: Option<Box<Trace>>,
54}
55
56/// Why a transform failed.
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum ErrorKind {
59    /// The map cannot be run on this input.
60    Failed,
61    /// It ran past one of the [`Limits`].
62    Limit,
63    /// The caller cancelled it.
64    Cancelled,
65}
66
67impl Error {
68    fn new(message: impl Into<String>) -> Self {
69        Self {
70            kind: ErrorKind::Failed,
71            message: message.into(),
72            map: None,
73            position: None,
74            trace: None,
75        }
76    }
77
78    fn limit(message: impl Into<String>) -> Self {
79        Self {
80            kind: ErrorKind::Limit,
81            ..Self::new(message)
82        }
83    }
84
85    /// Records where the error happened, unless a deeper part already did.
86    fn at(mut self, map: &StructureMap, position: &str) -> Self {
87        if self.position.is_none() {
88            self.map = map.url.clone();
89            self.position = Some(position.to_owned());
90        }
91        self
92    }
93
94    fn in_rule(rule: &Rule, message: impl fmt::Display) -> Self {
95        match &rule.name {
96            Some(name) => Self::new(format!("rule `{name}`: {message}")),
97            None => Self::new(message.to_string()),
98        }
99    }
100}
101
102impl fmt::Display for Error {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        f.write_str(&self.message)
105    }
106}
107
108impl std::error::Error for Error {}
109
110type Result<T> = std::result::Result<T, Error>;
111
112/// Finds the maps a map imports, by canonical URL or `*` pattern.
113pub trait MapResolver {
114    fn resolve(&self, url: &str) -> Vec<Arc<StructureMap>>;
115}
116
117/// No imported maps.
118#[derive(Debug, Default, Clone, Copy)]
119pub struct NoMaps;
120
121impl MapResolver for NoMaps {
122    fn resolve(&self, _url: &str) -> Vec<Arc<StructureMap>> {
123        Vec::new()
124    }
125}
126
127/// A fixed set of maps, matched by URL; `*` in an import matches any run of characters.
128#[derive(Debug, Default, Clone)]
129pub struct MapSet {
130    maps: Vec<Arc<StructureMap>>,
131}
132
133impl MapSet {
134    pub fn new() -> Self {
135        Self::default()
136    }
137
138    pub fn add(&mut self, map: StructureMap) {
139        self.maps.push(Arc::new(map));
140    }
141}
142
143impl MapResolver for MapSet {
144    fn resolve(&self, url: &str) -> Vec<Arc<StructureMap>> {
145        self.maps
146            .iter()
147            .filter(|map| {
148                map.url
149                    .as_deref()
150                    .is_some_and(|candidate| match url.split_once('*') {
151                        Some((prefix, suffix)) => {
152                            candidate.len() >= prefix.len() + suffix.len()
153                                && candidate.starts_with(prefix)
154                                && candidate.ends_with(suffix)
155                        }
156                        None => candidate == url,
157                    })
158            })
159            .cloned()
160            .collect()
161    }
162}
163
164/// How much a transform may do before it stops with an [`ErrorKind::Limit`] error, and how much
165/// of its trace is kept.
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167pub struct Limits {
168    /// Groups calling groups.
169    pub max_depth: usize,
170    /// Rules run, counting each run of a rule once.
171    pub max_rules: u64,
172    /// Values created: target elements and intermediate values.
173    pub max_values: usize,
174    /// Trace events kept; later events are left out and the trace is `truncated`.
175    pub max_trace_events: usize,
176    /// Roughly the size of the kept trace events as JSON, in bytes.
177    pub max_trace_bytes: usize,
178}
179
180impl Default for Limits {
181    fn default() -> Self {
182        Self {
183            max_depth: 512,
184            max_rules: 20_000_000,
185            max_values: 2_000_000,
186            max_trace_events: 10_000,
187            max_trace_bytes: 8 * 1024 * 1024,
188        }
189    }
190}
191
192/// The result of a transform.
193#[derive(Debug, Clone, PartialEq)]
194pub struct Transformed {
195    /// The target, as FHIR JSON.
196    pub target: Value,
197    /// Messages from `log (…)` clauses.
198    pub log: Vec<String>,
199    /// Values left out of the target, such as a code a ConceptMap does not know (the reference
200    /// engine leaves those unset too).
201    pub warnings: Vec<String>,
202    /// Resources the map created outside the target (`create('Observation') as obs` with no
203    /// target element) and did not put into it: the `produced` structures of the map.
204    pub produced: Vec<Value>,
205    /// The debug trace, when tracing is on ([`Engine::with_trace`]).
206    pub trace: Option<Trace>,
207}
208
209/// Runs StructureMaps.
210pub struct Engine<'a, X> {
211    definitions: &'a dyn Definitions,
212    expressions: X,
213    maps: &'a dyn MapResolver,
214    concept_maps: &'a dyn ConceptMapResolver,
215    /// `$translate` for ConceptMaps the resolver does not have.
216    translator: Option<&'a dyn TranslateService>,
217    ids: &'a dyn IdGenerator,
218    limits: Limits,
219    cancel: Option<&'a AtomicBool>,
220    trace: bool,
221    /// Whether a code a ConceptMap gives no usable target for is left out with a warning
222    /// instead of failing the transform (see
223    /// [`with_lenient_translations`](Self::with_lenient_translations)).
224    lenient_translations: bool,
225    /// Whether a template's version (`extension`) must match too (see
226    /// [`with_template_versions`](Self::with_template_versions)).
227    template_versions: bool,
228    /// Group lookups, which look through every imported map: the maps do not change between
229    /// transforms, so each answer is computed once.
230    groups: Mutex<GroupCache>,
231}
232
233/// A target type with its FHIR type name.
234type TypedTarget = (Type, Option<String>);
235
236/// Cached lookups of groups in imported maps, by the importing map's URL and imports. Groups of
237/// the map being run are looked up in it each time, so a changed map with the same URL is not
238/// answered from the cache.
239#[derive(Default)]
240struct GroupCache {
241    by_name: HashMap<(String, String), Found>,
242    by_types: HashMap<(String, String, String), Option<GroupRef>>,
243    default_target: HashMap<(String, String), Option<TypedTarget>>,
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247enum Mode {
248    Input,
249    Output,
250    Shared,
251}
252
253/// Variables in scope: later entries hide earlier ones.
254#[derive(Debug, Clone, Default)]
255struct Vars {
256    entries: Vec<(Mode, String, NodeId)>,
257}
258
259impl Vars {
260    fn add(&mut self, mode: Mode, name: &str, node: NodeId) {
261        self.entries.push((mode, name.to_owned(), node));
262    }
263
264    fn get(&self, mode: Mode, name: &str) -> Option<NodeId> {
265        self.entries
266            .iter()
267            .rev()
268            .find(|(m, n, _)| *m == mode && n == name)
269            .map(|(_, _, node)| *node)
270    }
271
272    /// An input, or an output used as an input, as the reference engine allows.
273    fn value(&self, name: &str) -> Option<NodeId> {
274        self.get(Mode::Input, name)
275            .or_else(|| self.get(Mode::Output, name))
276    }
277
278    fn names(&self) -> impl Iterator<Item = &str> {
279        self.entries
280            .iter()
281            .filter(|(mode, _, _)| *mode != Mode::Shared)
282            .map(|(_, name, _)| name.as_str())
283    }
284}
285
286/// A group looked up by name.
287#[derive(Clone)]
288enum Found {
289    One(GroupRef),
290    None,
291    /// The URLs of the imported maps that each have the group.
292    Several(Vec<String>),
293}
294
295/// A group of a map.
296#[derive(Clone)]
297struct GroupRef {
298    map: Arc<StructureMap>,
299    index: usize,
300}
301
302impl GroupRef {
303    fn group(&self) -> &Group {
304        &self.map.group[self.index]
305    }
306}
307
308/// State of one transform.
309#[derive(Default)]
310struct Run {
311    tree: Tree,
312    log: Vec<String>,
313    warnings: Vec<String>,
314    depth: usize,
315    /// Recorded events, when tracing.
316    trace: Option<Vec<TraceEvent>>,
317    trace_depth: usize,
318    /// Resources created outside the target.
319    produced: Vec<NodeId>,
320    /// Evaluated `let` constants, by map URL and name.
321    constants: HashMap<(String, String), Vec<NodeId>>,
322    /// Constants being evaluated, to report one that refers to itself.
323    evaluating: HashSet<(String, String)>,
324    /// Rules run so far.
325    rules: u64,
326    /// Roughly the size of the recorded trace events, in bytes.
327    trace_bytes: usize,
328    /// Why later trace events were left out.
329    trace_truncated: Option<String>,
330    /// Elements written by a `single` target: element, owner and target.
331    single: HashSet<(NodeId, String, usize)>,
332    /// The `first` and `last` values of repeating elements, by owner and element.
333    order: HashMap<(NodeId, String), TargetOrder>,
334}
335
336/// Where the `first` and `last` targets of one repeating element put their values.
337#[derive(Default)]
338struct TargetOrder {
339    first: Option<usize>,
340    first_count: usize,
341    last: Option<usize>,
342    last_values: Vec<NodeId>,
343}
344const CORE: &str = "http://hl7.org/fhir/StructureDefinition/";
345
346impl<'a, X: Expressions> Engine<'a, X> {
347    pub fn new(definitions: &'a dyn Definitions, expressions: X) -> Self {
348        Self {
349            definitions,
350            expressions,
351            maps: &NoMaps,
352            concept_maps: &NoConceptMaps,
353            translator: None,
354            ids: &RandomIds,
355            limits: Limits::default(),
356            cancel: None,
357            trace: false,
358            template_versions: true,
359            lenient_translations: false,
360            groups: Mutex::new(GroupCache::default()),
361        }
362    }
363
364    fn cache(&self) -> std::sync::MutexGuard<'_, GroupCache> {
365        self.groups.lock().unwrap_or_else(|e| e.into_inner())
366    }
367
368    /// Records a debug trace of each transform: see [`Trace`].
369    pub fn with_trace(mut self, trace: bool) -> Self {
370        self.trace = trace;
371        self
372    }
373
374    /// Where `translate` finds ConceptMaps that are not contained in the map.
375    pub fn with_concept_maps(mut self, concept_maps: &'a dyn ConceptMapResolver) -> Self {
376        self.concept_maps = concept_maps;
377        self
378    }
379
380    /// A terminology service for `translate` with ConceptMaps neither the map nor the resolver
381    /// holds.
382    pub fn with_translator(mut self, translator: &'a dyn TranslateService) -> Self {
383        self.translator = Some(translator);
384        self
385    }
386
387    /// How much a transform may do; see [`Limits`].
388    pub fn with_limits(mut self, limits: Limits) -> Self {
389        self.limits = limits;
390        self
391    }
392
393    /// A flag the caller sets to stop running transforms; they fail with
394    /// [`ErrorKind::Cancelled`] at the next rule.
395    pub fn with_cancellation(mut self, cancel: &'a AtomicBool) -> Self {
396        self.cancel = Some(cancel);
397        self
398    }
399
400    /// Whether a value carries a template only with the template's version (`templateId`
401    /// `extension`), as a profile's identifier states it — the default — or with any version of
402    /// it (the same `root`). Converters that must recognise documents written against earlier
403    /// versions of the templates turn versions off.
404    pub fn with_template_versions(mut self, versions: bool) -> Self {
405        self.template_versions = versions;
406        self
407    }
408
409    /// What `translate` does with a code whose ConceptMap element has no usable target (R4
410    /// `unmatched` or `disjoint`, R5 `not-related-to`, or several): fail the transform, as the
411    /// reference engine does (the default), or leave the target unset and report a warning, as
412    /// for a code the ConceptMap does not list. Converters of whole documents leave it out.
413    pub fn with_lenient_translations(mut self, lenient: bool) -> Self {
414        self.lenient_translations = lenient;
415        self
416    }
417
418    /// Where `uuid()` and new resource ids come from; random by default.
419    pub fn with_ids(mut self, ids: &'a dyn IdGenerator) -> Self {
420        self.ids = ids;
421        self
422    }
423
424    /// Where the maps named in `imports` come from.
425    pub fn with_maps(mut self, maps: &'a dyn MapResolver) -> Self {
426        self.maps = maps;
427        self
428    }
429
430    fn types(&self) -> Types<'a> {
431        Types::new(self.definitions)
432    }
433
434    /// Transforms `source` with the map's first group; the target is created from the type of
435    /// that group's target parameter.
436    pub async fn transform(&self, map: &StructureMap, source: &Value) -> Result<Transformed> {
437        self.run(map, source, None).await
438    }
439
440    /// Transforms `source` into an existing `target`.
441    pub async fn transform_into(
442        &self,
443        map: &StructureMap,
444        source: &Value,
445        target: &Value,
446    ) -> Result<Transformed> {
447        self.run(map, source, Some(target)).await
448    }
449
450    async fn run(
451        &self,
452        map: &StructureMap,
453        source: &Value,
454        target: Option<&Value>,
455    ) -> Result<Transformed> {
456        let map = Arc::new(map.clone());
457        let group = map
458            .group
459            .first()
460            .ok_or_else(|| Error::new("the map has no groups"))?;
461        let input = |mode: &str| group.input.iter().find(|input| input.mode == mode);
462        let source_input =
463            input("source").ok_or_else(|| Error::new("the first group has no source"))?;
464        let target_input =
465            input("target").ok_or_else(|| Error::new("the first group has no target"))?;
466
467        let mut run = Run {
468            trace: self.trace.then(Vec::new),
469            ..Run::default()
470        };
471        let types = self.types();
472        let source_type = self.input_type(&map, source_input.type_name.as_deref());
473        let source_node = run.tree.load(source, source_type, &types);
474        if run.trace.is_some() {
475            // The trace says where an input variable is in the input (debug.md, variable.path).
476            // A logical model's type is a URL; its paths start with the structure's name, as
477            // the XML reader records positions (`ClinicalDocument.component…`).
478            let root_name = match run.tree.ty(source_node) {
479                Type::Node { structure, path }
480                    if path.is_empty() && structure.kind == Kind::Logical =>
481                {
482                    structure.name.clone()
483                }
484                _ => run
485                    .tree
486                    .fhir_type(source_node)
487                    .unwrap_or_else(|| "%resource".to_owned()),
488            };
489            run.tree.record_paths(source_node, &root_name);
490        }
491        let target_type = self.input_type(&map, target_input.type_name.as_deref());
492        let target_node = match target {
493            Some(target) => run.tree.load(target, target_type, &types),
494            None => {
495                if matches!(target_type, Type::Unknown) {
496                    return Err(Error::new(format!(
497                        "cannot create the target: type `{}` is not known",
498                        target_input.type_name.as_deref().unwrap_or("?")
499                    )));
500                }
501                run.tree.create(target_type, None)
502            }
503        };
504        let mut vars = Vars::default();
505        vars.add(Mode::Input, &source_input.name, source_node);
506        vars.add(Mode::Output, &target_input.name, target_node);
507        let entry = GroupRef {
508            map: map.clone(),
509            index: 0,
510        };
511        if let Err(mut error) = self.execute_group(&mut run, entry, vars, true).await {
512            let truncated = run.trace_truncated.take();
513            error.trace = run.trace.take().map(|events| {
514                Box::new(Trace {
515                    status: match truncated {
516                        Some(_) => TraceStatus::Truncated,
517                        None => TraceStatus::Stopped,
518                    },
519                    note: truncated,
520                    events,
521                })
522            });
523            return Err(error);
524        }
525        let in_target = run.tree.reachable(target_node);
526        let produced = run
527            .produced
528            .iter()
529            .filter(|node| !in_target.contains(node))
530            .map(|&node| run.tree.to_json(node))
531            .collect();
532        Ok(Transformed {
533            target: run.tree.to_json(target_node),
534            log: run.log,
535            warnings: run.warnings,
536            produced,
537            trace: run.trace.map(|events| Trace {
538                status: match run.trace_truncated {
539                    Some(_) => TraceStatus::Truncated,
540                    None => TraceStatus::Complete,
541                },
542                note: run.trace_truncated,
543                events,
544            }),
545        })
546    }
547
548    /// The type a group parameter or `create` names: a `uses` alias, a URL, or a type name.
549    fn input_type(&self, map: &StructureMap, name: Option<&str>) -> Type {
550        let Some(name) = name else {
551            return Type::Unknown;
552        };
553        self.types().structure(&self.type_name_url(map, name, CORE))
554    }
555
556    fn type_name_url(&self, map: &StructureMap, name: &str, context: &str) -> String {
557        if let Some(structure) = map
558            .structure
559            .iter()
560            .find(|s| s.alias.as_deref() == Some(name))
561        {
562            return structure.url.clone();
563        }
564        type_url(name, context)
565    }
566
567    // ---- Groups -----------------------------------------------------------------------------
568
569    fn execute_group<'r>(
570        &'r self,
571        run: &'r mut Run,
572        group: GroupRef,
573        vars: Vars,
574        at_root: bool,
575    ) -> BoxFuture<'r, Result<()>> {
576        Box::pin(async move {
577            run.depth += 1;
578            if run.depth > self.limits.max_depth {
579                return Err(Error::limit(format!(
580                    "groups nest more than {} deep; is a group calling itself without end?",
581                    self.limits.max_depth
582                )));
583            }
584            let map = group.map.clone();
585            let definition = group.group();
586            let position = format!("StructureMap.group[{}]", group.index);
587            self.record(run, Phase::Enter, &map, &position, &vars, Vec::new());
588            if let Some(extends) = &definition.extends {
589                let base = self.group_by_name(&map, extends)?;
590                self.execute_group(run, base, vars.clone(), false).await?;
591            }
592            let mut vars = vars;
593            for (index, rule) in definition.rule.iter().enumerate() {
594                let rule_position = format!("{position}.rule[{index}]");
595                self.execute_rule(run, &group, rule, &rule_position, &mut vars, at_root)
596                    .await?;
597            }
598            self.record(run, Phase::Exit, &map, &position, &vars, Vec::new());
599            run.depth -= 1;
600            Ok(())
601        })
602    }
603
604    /// The group `name`, in the map or the maps it imports.
605    fn group_by_name(&self, map: &Arc<StructureMap>, name: &str) -> Result<GroupRef> {
606        if let Some(index) = map.group.iter().position(|g| g.name == name) {
607            return Ok(GroupRef {
608                map: map.clone(),
609                index,
610            });
611        }
612        let key = (imports_key(map), name.to_owned());
613        let cached = self.cache().by_name.get(&key).cloned();
614        let found = match cached {
615            Some(found) => found,
616            None => {
617                let found = self.find_imported_group_by_name(map, name);
618                self.cache().by_name.insert(key, found.clone());
619                found
620            }
621        };
622        match found {
623            Found::One(group) => Ok(group),
624            Found::None => Err(Error::new(format!(
625                "no group `{name}` in {} or the maps it imports",
626                map.url.as_deref().unwrap_or("the map")
627            ))),
628            Found::Several(urls) => Err(Error::new(format!(
629                "group `{name}` is in several imported maps ({}); a group name must be unique",
630                urls.join(", ")
631            ))),
632        }
633    }
634
635    /// The group in exactly one of the maps `map` imports (fml.md: a group name must be unique
636    /// among the imported maps).
637    fn find_imported_group_by_name(&self, map: &Arc<StructureMap>, name: &str) -> Found {
638        let mut found: Vec<GroupRef> = Vec::new();
639        for import in &map.import {
640            for imported in self.maps.resolve(import) {
641                if imported.url == map.url || found.iter().any(|g| g.map.url == imported.url) {
642                    continue;
643                }
644                if let Some(index) = imported.group.iter().position(|g| g.name == name) {
645                    found.push(GroupRef {
646                        map: imported.clone(),
647                        index,
648                    });
649                }
650            }
651        }
652        match found.len() {
653            0 => Found::None,
654            1 => Found::One(found.remove(0)),
655            _ => Found::Several(
656                found
657                    .iter()
658                    .map(|g| g.map.url.clone().unwrap_or_default())
659                    .collect(),
660            ),
661        }
662    }
663
664    /// The default group for a source and target type: one with a type mode whose parameter
665    /// types match.
666    fn group_by_types(
667        &self,
668        map: &Arc<StructureMap>,
669        source: Option<&str>,
670        target: Option<&str>,
671    ) -> Option<GroupRef> {
672        let (source, target) = (source?, target?);
673        if let Some(index) = self.default_group_index(map, source, target) {
674            return Some(GroupRef {
675                map: map.clone(),
676                index,
677            });
678        }
679        let key = (imports_key(map), source.to_owned(), target.to_owned());
680        if let Some(found) = self.cache().by_types.get(&key) {
681            return found.clone();
682        }
683        let found = self.find_imported_group_by_types(map, source, target);
684        self.cache().by_types.insert(key, found.clone());
685        found
686    }
687
688    /// The default group of `map` itself for the types.
689    fn default_group_index(&self, map: &StructureMap, source: &str, target: &str) -> Option<usize> {
690        map.group.iter().position(|group| {
691            group.type_mode.is_some()
692                && matches!(group.input.as_slice(), [s, t]
693                    if s.mode == "source" && t.mode == "target"
694                    && self.stated_type_is(map, s.type_name.as_deref(), source)
695                    && !self.is_template_profile(map, s.type_name.as_deref())
696                    && self.stated_type_is(map, t.type_name.as_deref(), target))
697        })
698    }
699
700    /// Whether a group parameter's type is a profile applied by template id: such groups are
701    /// chosen by the value's templates, not by its type alone.
702    fn is_template_profile(&self, map: &StructureMap, stated: Option<&str>) -> bool {
703        stated.is_some_and(|stated| {
704            !self
705                .definitions
706                .templates(&self.type_name_url(map, stated, CORE))
707                .is_empty()
708        })
709    }
710
711    fn find_imported_group_by_types(
712        &self,
713        map: &Arc<StructureMap>,
714        source: &str,
715        target: &str,
716    ) -> Option<GroupRef> {
717        for import in &map.import {
718            for imported in self.maps.resolve(import) {
719                if imported.url == map.url {
720                    continue;
721                }
722                if let Some(index) = self.default_group_index(&imported, source, target) {
723                    return Some(GroupRef {
724                        map: imported,
725                        index,
726                    });
727                }
728            }
729        }
730        None
731    }
732
733    /// The type a source type filter names: through a `uses` alias to the structure's type
734    /// (`: CodeableConceptR4` is `CodeableConcept`), or the name as written.
735    fn stated_type_name(&self, map: &StructureMap, stated: &str) -> String {
736        map.structure
737            .iter()
738            .find(|s| s.alias.as_deref() == Some(stated))
739            .and_then(|s| self.definitions.structure(&s.url))
740            .map_or_else(|| stated.to_owned(), |s| s.type_name.clone())
741    }
742
743    /// Whether a value has the type a source type filter states: the type itself (by type name,
744    /// or for a logical model also by structure name, `CD`), and for a profile applied by
745    /// template id, one of the profile's templates.
746    fn has_stated_type(&self, run: &Run, map: &StructureMap, node: NodeId, stated: &str) -> bool {
747        let wanted = self.stated_type_name(map, stated);
748        let same = run.tree.fhir_type(node).as_deref() == Some(wanted.as_str())
749            || matches!(run.tree.ty(node), Type::Node { structure, path }
750                if path.is_empty()
751                    && structure.kind == Kind::Logical
752                    && (structure.name == wanted || structure.url == wanted));
753        same && self.has_templates_of(run, map, node, stated)
754    }
755
756    /// Whether a value carries one of the templates of the profile `stated` names; true when the
757    /// profile is not applied by template id.
758    fn has_templates_of(&self, run: &Run, map: &StructureMap, node: NodeId, stated: &str) -> bool {
759        let url = self.type_name_url(map, stated, CORE);
760        let templates = self.definitions.templates(&url);
761        if templates.is_empty() {
762            return true;
763        }
764        run.tree.children(node, "templateId").into_iter().any(|id| {
765            let text = |name: &str| {
766                run.tree
767                    .children(id, name)
768                    .first()
769                    .and_then(|&n| run.tree.text(n))
770            };
771            let (root, extension) = (text("root"), text("extension"));
772            root.is_some_and(|root| {
773                templates.iter().any(|t| {
774                    if self.template_versions {
775                        t.matches(&root, extension.as_deref())
776                    } else {
777                        t.root == root
778                    }
779                })
780            })
781        })
782    }
783
784    /// The default group for a value whose type has profiles applied by template id: the first
785    /// with a type mode whose source is such a profile the value carries a template of, and whose
786    /// target is the target's type.
787    fn group_by_template(
788        &self,
789        run: &Run,
790        map: &Arc<StructureMap>,
791        source: NodeId,
792        target: Option<&str>,
793    ) -> Option<GroupRef> {
794        let target = target?;
795        let fits = |owner: &StructureMap, group: &Group| -> bool {
796            group.type_mode.is_some()
797                && matches!(group.input.as_slice(), [s, t]
798                    if s.mode == "source" && t.mode == "target"
799                    && s.type_name.as_deref().is_some_and(|stated| {
800                        !self.definitions.templates(&self.type_name_url(owner, stated, CORE)).is_empty()
801                            && self.has_stated_type(run, owner, source, stated)
802                    })
803                    && self.stated_type_is(owner, t.type_name.as_deref(), target))
804        };
805        if let Some(index) = map.group.iter().position(|g| fits(map, g)) {
806            return Some(GroupRef {
807                map: map.clone(),
808                index,
809            });
810        }
811        for import in &map.import {
812            for imported in self.maps.resolve(import) {
813                if imported.url == map.url {
814                    continue;
815                }
816                if let Some(index) = imported.group.iter().position(|g| fits(&imported, g)) {
817                    return Some(GroupRef {
818                        map: imported,
819                        index,
820                    });
821                }
822            }
823        }
824        None
825    }
826
827    fn stated_type_is(&self, map: &StructureMap, stated: Option<&str>, actual: &str) -> bool {
828        let Some(stated) = stated else { return false };
829        if stated == actual {
830            return true;
831        }
832        let url = self.type_name_url(map, stated, CORE);
833        self.definitions
834            .structure(&url)
835            .is_some_and(|s| s.type_name == actual)
836    }
837
838    // ---- Rules ------------------------------------------------------------------------------
839
840    fn execute_rule<'r>(
841        &'r self,
842        run: &'r mut Run,
843        group: &'r GroupRef,
844        rule: &'r Rule,
845        position: &'r str,
846        vars: &'r mut Vars,
847        at_root: bool,
848    ) -> BoxFuture<'r, Result<()>> {
849        Box::pin(async move {
850            self.check_limits(run)?;
851            self.record(run, Phase::Enter, &group.map, position, vars, Vec::new());
852            let mut bindings = vec![vars.clone()];
853            for (index, source) in rule.source.iter().enumerate() {
854                let logged = run.log.len();
855                let mut next = Vec::new();
856                for binding in &bindings {
857                    next.extend(
858                        self.process_source(run, group, rule, source, binding)
859                            .await
860                            .map_err(|e| {
861                                e.at(&group.map, &format!("{position}.source[{index}]"))
862                            })?,
863                    );
864                }
865                bindings = next;
866                if run.trace.is_some() {
867                    let messages = run.log[logged..].to_vec();
868                    let shown = bindings.first().cloned().unwrap_or_else(|| vars.clone());
869                    let step = format!("{position}.source[{index}]");
870                    self.record(run, Phase::Step, &group.map, &step, &shown, messages);
871                }
872            }
873            let source_variable = match rule.source.as_slice() {
874                [single] => single.variable.as_deref(),
875                _ => None,
876            };
877            for mut binding in bindings {
878                for (index, target) in rule.target.iter().enumerate() {
879                    self.process_target(
880                        run,
881                        group,
882                        rule,
883                        target,
884                        &mut binding,
885                        vars,
886                        source_variable,
887                        at_root,
888                    )
889                    .await
890                    .map_err(|e| e.at(&group.map, &format!("{position}.target[{index}]")))?;
891                    if run.trace.is_some() {
892                        let step = format!("{position}.target[{index}]");
893                        self.record(run, Phase::Step, &group.map, &step, &binding, Vec::new());
894                    }
895                }
896                // The named groups run first, then the nested rules (fml.md, "Dependent Rules").
897                for dependent in &rule.dependent {
898                    self.execute_dependency(run, group, rule, dependent, &binding)
899                        .await
900                        .map_err(|e| e.at(&group.map, position))?;
901                }
902                for (index, child) in rule.rule.iter().enumerate() {
903                    let child_position = format!("{position}.rule[{index}]");
904                    self.execute_rule(run, group, child, &child_position, &mut binding, false)
905                        .await?;
906                }
907                if rule.dependent.is_empty()
908                    && rule.rule.is_empty()
909                    && let Some((source, target)) = simple_copy(rule)
910                {
911                    self.default_copy(run, group, rule, source, target, &binding)
912                        .await
913                        .map_err(|e| e.at(&group.map, position))?;
914                }
915            }
916            self.record(run, Phase::Exit, &group.map, position, vars, Vec::new());
917            Ok(())
918        })
919    }
920
921    /// Stops the transform when the caller cancelled it or it ran past its limits.
922    fn check_limits(&self, run: &mut Run) -> Result<()> {
923        if self
924            .cancel
925            .is_some_and(|cancel| cancel.load(Ordering::Relaxed))
926        {
927            return Err(Error {
928                kind: ErrorKind::Cancelled,
929                ..Error::new("the transform was cancelled")
930            });
931        }
932        run.rules += 1;
933        if run.rules > self.limits.max_rules {
934            return Err(Error::limit(format!(
935                "the transform ran more than {} rules",
936                self.limits.max_rules
937            )));
938        }
939        if run.tree.len() > self.limits.max_values {
940            return Err(Error::limit(format!(
941                "the transform created more than {} values",
942                self.limits.max_values
943            )));
944        }
945        Ok(())
946    }
947
948    /// Records a trace event, when tracing.
949    fn record(
950        &self,
951        run: &mut Run,
952        phase: Phase,
953        map: &StructureMap,
954        position: &str,
955        vars: &Vars,
956        messages: Vec<String>,
957    ) {
958        if run.trace.is_none() {
959            return;
960        }
961        if phase == Phase::Exit {
962            run.trace_depth = run.trace_depth.saturating_sub(1);
963        }
964        if run.trace_truncated.is_some() {
965            if phase == Phase::Enter {
966                run.trace_depth += 1;
967            }
968            return;
969        }
970        let mut seen = HashSet::new();
971        let mut variables: Vec<TraceVariable> = vars
972            .entries
973            .iter()
974            .rev()
975            .filter(|(mode, name, _)| *mode != Mode::Shared && seen.insert(name.clone()))
976            .map(|(mode, name, node)| TraceVariable {
977                name: name.clone(),
978                context_type: match mode {
979                    Mode::Input => "input",
980                    _ => "output",
981                }
982                .to_owned(),
983                value: run.tree.to_json(*node),
984                data_type: self.item(run, *node).type_name,
985                path: run.tree.path(*node).map(str::to_owned),
986            })
987            .collect();
988        variables.reverse();
989        let event = TraceEvent {
990            phase,
991            depth: run.trace_depth,
992            source: map.url.clone(),
993            position: position.to_owned(),
994            variables,
995            messages,
996        };
997        if run.trace_truncated.is_none() {
998            let size = event.approximate_size();
999            let count = run.trace.as_ref().map_or(0, Vec::len);
1000            if count >= self.limits.max_trace_events {
1001                run.trace_truncated = Some(format!(
1002                    "the trace keeps its first {} events",
1003                    self.limits.max_trace_events
1004                ));
1005            } else if run.trace_bytes + size > self.limits.max_trace_bytes {
1006                run.trace_truncated = Some(format!(
1007                    "the trace keeps its first {count} events, about {} bytes",
1008                    run.trace_bytes
1009                ));
1010            } else if let Some(events) = &mut run.trace {
1011                run.trace_bytes += size;
1012                events.push(event);
1013            }
1014        }
1015        if phase == Phase::Enter {
1016            run.trace_depth += 1;
1017        }
1018    }
1019
1020    /// `src.a as s -> tgt.a as t` with nothing else to do: the default group for the two types
1021    /// maps one into the other, or the value is copied when the types are the same.
1022    async fn default_copy(
1023        &self,
1024        run: &mut Run,
1025        group: &GroupRef,
1026        rule: &Rule,
1027        source: &str,
1028        target: &str,
1029        vars: &Vars,
1030    ) -> Result<()> {
1031        let source = vars
1032            .get(Mode::Input, source)
1033            .ok_or_else(|| Error::in_rule(rule, format!("no source variable `{source}`")))?;
1034        let target = vars
1035            .get(Mode::Output, target)
1036            .ok_or_else(|| Error::in_rule(rule, format!("no target variable `{target}`")))?;
1037        let source_type = run.tree.fhir_type(source);
1038        let target_type = run.tree.fhir_type(target);
1039        let chosen = self
1040            .group_by_template(run, &group.map, source, target_type.as_deref())
1041            .or_else(|| {
1042                self.group_by_types(&group.map, source_type.as_deref(), target_type.as_deref())
1043            });
1044        if let Some(default) = chosen {
1045            let inputs = &default.group().input;
1046            let mut call = Vars::default();
1047            call.add(Mode::Input, &inputs[0].name, source);
1048            call.add(Mode::Output, &inputs[1].name, target);
1049            return self.execute_group(run, default, call, false).await;
1050        }
1051        if source_type == target_type || target_type.is_none() {
1052            run.tree.assign(target, source);
1053            return Ok(());
1054        }
1055        Err(Error::in_rule(
1056            rule,
1057            format!(
1058                "no default group maps {} to {}",
1059                source_type.as_deref().unwrap_or("?"),
1060                target_type.as_deref().unwrap_or("?")
1061            ),
1062        ))
1063    }
1064
1065    async fn execute_dependency(
1066        &self,
1067        run: &mut Run,
1068        group: &GroupRef,
1069        rule: &Rule,
1070        dependent: &Dependent,
1071        vars: &Vars,
1072    ) -> Result<()> {
1073        let callee = self.group_by_name(&group.map, &dependent.name)?;
1074        let inputs = &callee.group().input;
1075        if inputs.len() != dependent.parameter.len() {
1076            return Err(Error::in_rule(
1077                rule,
1078                format!(
1079                    "group `{}` takes {} arguments, the call passes {}",
1080                    dependent.name,
1081                    inputs.len(),
1082                    dependent.parameter.len()
1083                ),
1084            ));
1085        }
1086        let mut call = Vars::default();
1087        for (input, parameter) in inputs.iter().zip(&dependent.parameter) {
1088            let name = parameter_name(parameter);
1089            let (mode, node) = if input.mode == "source" {
1090                // A literal argument (fml.md: a parameter is an id or a literal) is a value of
1091                // its own; a string names a variable when one has that name.
1092                let node = match &parameter.value {
1093                    ParameterValue::Id(name) => vars.value(name),
1094                    ParameterValue::String(text) => vars
1095                        .value(text)
1096                        .or_else(|| Some(self.string(run, text.clone(), "string"))),
1097                    ParameterValue::Boolean(b) => {
1098                        Some(self.literal(run, Value::Bool(*b), "boolean"))
1099                    }
1100                    ParameterValue::Integer(n) => {
1101                        Some(self.literal(run, Value::from(*n), "integer"))
1102                    }
1103                    ParameterValue::Decimal(n) => {
1104                        Some(self.literal(run, Value::Number(n.clone()), "decimal"))
1105                    }
1106                    ParameterValue::Date(text) => Some(self.string(run, text.clone(), "date")),
1107                    ParameterValue::Time(text) => Some(self.string(run, text.clone(), "time")),
1108                    ParameterValue::DateTime(text) => {
1109                        Some(self.string(run, text.clone(), "dateTime"))
1110                    }
1111                };
1112                (Mode::Input, node)
1113            } else {
1114                (Mode::Output, vars.get(Mode::Output, &name))
1115            };
1116            let node = node.ok_or_else(|| {
1117                Error::in_rule(
1118                    rule,
1119                    format!(
1120                        "`{}` passes `{name}` as {} `{}`, but it has no value",
1121                        dependent.name, input.mode, input.name
1122                    ),
1123                )
1124            })?;
1125            call.add(mode, &input.name, node);
1126        }
1127        self.execute_group(run, callee, call, false).await
1128    }
1129
1130    // ---- Sources ----------------------------------------------------------------------------
1131
1132    async fn process_source(
1133        &self,
1134        run: &mut Run,
1135        group: &GroupRef,
1136        rule: &Rule,
1137        source: &Source,
1138        vars: &Vars,
1139    ) -> Result<Vec<Vars>> {
1140        let context = vars.get(Mode::Input, &source.context).ok_or_else(|| {
1141            Error::in_rule(
1142                rule,
1143                format!("unknown source variable `{}`", source.context),
1144            )
1145        })?;
1146        let mut items = vec![context];
1147        for element in element_path(
1148            source.element.as_deref(),
1149            &source.extension,
1150            SOURCE_ELEMENT_EXTENSION,
1151        ) {
1152            items = items
1153                .iter()
1154                .flat_map(|&item| run.tree.children(item, &element))
1155                .collect();
1156        }
1157        check_cardinality(rule, source, items.len())?;
1158        // The type filter selects the exact type; the default stands in when nothing is left.
1159        if let Some(type_name) = &source.type_name {
1160            items.retain(|&item| self.has_stated_type(run, &group.map, item, type_name));
1161        }
1162        if items.is_empty()
1163            && let Some(default) = &source.default_value
1164        {
1165            items = self.evaluate(run, group, default, None, vars).await?;
1166        }
1167        let with_item = |item: NodeId| {
1168            let mut bound = vars.clone();
1169            if let Some(variable) = &source.variable {
1170                bound.add(Mode::Input, variable, item);
1171            }
1172            bound
1173        };
1174        if let Some(condition) = &source.condition {
1175            let mut kept = Vec::with_capacity(items.len());
1176            for item in items {
1177                if self
1178                    .evaluate_boolean(run, group, condition, Some(item), &with_item(item))
1179                    .await?
1180                {
1181                    kept.push(item);
1182                }
1183            }
1184            items = kept;
1185        }
1186        if let Some(check) = &source.check {
1187            for &item in &items {
1188                if !self
1189                    .evaluate_boolean(run, group, check, Some(item), &with_item(item))
1190                    .await?
1191                {
1192                    return Err(Error::in_rule(rule, format!("check `{check}` failed")));
1193                }
1194            }
1195        }
1196        if let Some(log) = &source.log_message {
1197            let mut messages = Vec::new();
1198            for &item in &items {
1199                let values = self
1200                    .evaluate(run, group, log, Some(item), &with_item(item))
1201                    .await?;
1202                messages.extend(values.into_iter().filter_map(|v| run.tree.text(v)));
1203            }
1204            if !messages.is_empty() {
1205                run.log.push(messages.join(","));
1206            }
1207        }
1208        match source.list_mode.as_deref() {
1209            _ if items.is_empty() => {}
1210            Some("first") => items.truncate(1),
1211            Some("not_first") => {
1212                items.remove(0);
1213            }
1214            Some("last") => items = vec![*items.last().unwrap()],
1215            Some("not_last") => {
1216                items.pop();
1217            }
1218            Some("only_one") if items.len() > 1 => {
1219                return Err(Error::in_rule(
1220                    rule,
1221                    format!("`{}` has more than one item", source.context),
1222                ));
1223            }
1224            _ => {}
1225        }
1226        Ok(items.into_iter().map(with_item).collect())
1227    }
1228
1229    // ---- Targets ----------------------------------------------------------------------------
1230
1231    #[allow(clippy::too_many_arguments)]
1232    async fn process_target(
1233        &self,
1234        run: &mut Run,
1235        group: &GroupRef,
1236        rule: &Rule,
1237        target: &Target,
1238        vars: &mut Vars,
1239        shared: &mut Vars,
1240        source_variable: Option<&str>,
1241        at_root: bool,
1242    ) -> Result<()> {
1243        let types = self.types();
1244        let elements = element_path(
1245            target.element.as_deref(),
1246            &target.extension,
1247            TARGET_ELEMENT_EXTENSION,
1248        );
1249        let element = elements.last().cloned();
1250        let mut destination = match &target.context {
1251            Some(context) => Some(vars.get(Mode::Output, context).ok_or_else(|| {
1252                Error::in_rule(rule, format!("unknown target variable `{context}`"))
1253            })?),
1254            None => None,
1255        };
1256        let sharing = target.list_mode.iter().any(|m| m == "share");
1257        let mut value = match (&target.transform, sharing, &target.list_rule_id) {
1258            (None, true, Some(id)) => shared.get(Mode::Shared, id),
1259            _ => None,
1260        };
1261        if let Some(dest) = destination
1262            && value.is_none()
1263        {
1264            let mut dest = dest;
1265            for intermediate in elements.iter().take(elements.len().saturating_sub(1)) {
1266                dest = run
1267                    .tree
1268                    .make_property(dest, intermediate, &types)
1269                    .map_err(|e| Error::in_rule(rule, e))?;
1270            }
1271            destination = Some(dest);
1272        }
1273        if target.transform.is_some() {
1274            let produced = if target.transform.as_deref() == Some("evaluate") {
1275                let values = self
1276                    .evaluate_transform(run, group, rule, target, vars)
1277                    .await?;
1278                if values.len() > 1 {
1279                    self.check_several(
1280                        run,
1281                        rule,
1282                        target,
1283                        destination,
1284                        element.as_deref(),
1285                        values.len(),
1286                    )?;
1287                }
1288                values
1289            } else {
1290                self.run_transform(
1291                    run,
1292                    group,
1293                    rule,
1294                    target,
1295                    vars,
1296                    destination,
1297                    element.as_deref(),
1298                    source_variable,
1299                    at_root,
1300                )
1301                .await?
1302                .into_iter()
1303                .collect()
1304            };
1305            if let Some(dest) = destination {
1306                for &item in &produced {
1307                    match &element {
1308                        Some(element) => {
1309                            run.tree
1310                                .set_property(dest, element, item, &types)
1311                                .map_err(|e| Error::in_rule(rule, e))?;
1312                            order_target(run, rule, target, dest, element, item)?;
1313                        }
1314                        None => run.tree.assign(dest, item),
1315                    }
1316                }
1317            }
1318            value = produced.last().copied();
1319        } else if let (Some(dest), None) = (destination, value) {
1320            value = Some(match &element {
1321                Some(element) => {
1322                    let created = run
1323                        .tree
1324                        .make_property(dest, element, &types)
1325                        .map_err(|e| Error::in_rule(rule, e))?;
1326                    if let (true, Some(id)) = (sharing, &target.list_rule_id) {
1327                        // Siblings find it in the enclosing scope, nested rules in this one.
1328                        shared.add(Mode::Shared, id, created);
1329                        vars.add(Mode::Shared, id, created);
1330                    }
1331                    order_target(run, rule, target, dest, element, created)?;
1332                    created
1333                }
1334                None => dest,
1335            });
1336        }
1337        if let (Some(variable), Some(value)) = (&target.variable, value) {
1338            vars.add(Mode::Output, variable, value);
1339        }
1340        Ok(())
1341    }
1342
1343    /// `evaluate(context, expression)` or `(expression)`: every value the expression gives.
1344    async fn evaluate_transform(
1345        &self,
1346        run: &mut Run,
1347        group: &GroupRef,
1348        rule: &Rule,
1349        target: &Target,
1350        vars: &Vars,
1351    ) -> Result<Vec<NodeId>> {
1352        let parameters = &target.parameter;
1353        let expression = match parameters.last().map(|p| &p.value) {
1354            Some(ParameterValue::String(expression)) => expression,
1355            _ => return Err(Error::in_rule(rule, "evaluate needs an expression")),
1356        };
1357        let focus = match parameters.as_slice() {
1358            [context, _] => Some(self.parameter_node(run, group, context, vars).await?),
1359            _ => None,
1360        };
1361        self.evaluate(run, group, expression, focus, vars).await
1362    }
1363
1364    /// Several values from `evaluate` make one target each, which needs a repeating element
1365    /// (transforms.md, "evaluate"); a variable cannot name them all.
1366    fn check_several(
1367        &self,
1368        run: &Run,
1369        rule: &Rule,
1370        target: &Target,
1371        destination: Option<NodeId>,
1372        element: Option<&str>,
1373        count: usize,
1374    ) -> Result<()> {
1375        let fail = |message: String| Err(Error::in_rule(rule, message));
1376        let (Some(dest), Some(element)) = (destination, element) else {
1377            return fail(format!(
1378                "evaluate gave {count} values where one was expected"
1379            ));
1380        };
1381        let owner = run.tree.ty(dest).display();
1382        match self.types().lookup(run.tree.ty(dest), element) {
1383            Lookup::Found(found) if found.element.array => {}
1384            Lookup::Found(_) => {
1385                return fail(format!(
1386                    "evaluate gave {count} values, but `{owner}.{element}` does not repeat"
1387                ));
1388            }
1389            _ => {
1390                return fail(format!(
1391                    "evaluate gave {count} values, and whether `{owner}.{element}` repeats is not known"
1392                ));
1393            }
1394        }
1395        if let Some(variable) = &target.variable {
1396            return fail(format!(
1397                "evaluate gave {count} values; variable `{variable}` can only name one"
1398            ));
1399        }
1400        Ok(())
1401    }
1402
1403    #[allow(clippy::too_many_arguments)]
1404    async fn run_transform(
1405        &self,
1406        run: &mut Run,
1407        group: &GroupRef,
1408        rule: &Rule,
1409        target: &Target,
1410        vars: &Vars,
1411        destination: Option<NodeId>,
1412        element: Option<&str>,
1413        source_variable: Option<&str>,
1414        _at_root: bool,
1415    ) -> Result<Option<NodeId>> {
1416        let transform = target.transform.as_deref().unwrap_or("copy");
1417        let parameters = &target.parameter;
1418        let fail = |message: String| Error::in_rule(rule, message);
1419        let types = self.types();
1420        match transform {
1421            "create" => {
1422                let ty = match parameters.first() {
1423                    Some(parameter) => {
1424                        let name = self.parameter_text(run, group, parameter, vars).await?;
1425                        let context = destination
1426                            .and_then(|d| run.tree.ty(d).structure().map(|s| s.url.clone()))
1427                            .unwrap_or_else(|| CORE.to_owned());
1428                        // A type of the element written to (`value = create('PQ')` into a
1429                        // logical model's element that has several), else by name or alias.
1430                        let of_element = match (destination, element) {
1431                            (Some(d), Some(element)) => match types.lookup(run.tree.ty(d), element)
1432                            {
1433                                Lookup::Found(found) => types.named_type(&found, &name),
1434                                _ => Type::Unknown,
1435                            },
1436                            _ => Type::Unknown,
1437                        };
1438                        let ty = match of_element {
1439                            Type::Unknown => {
1440                                types.structure(&self.type_name_url(&group.map, &name, &context))
1441                            }
1442                            known => known,
1443                        };
1444                        (ty, Some(name))
1445                    }
1446                    None => self
1447                        .implied_type(run, group, destination, element, source_variable, vars)
1448                        .map_err(fail)?,
1449                };
1450                let created = run.tree.create(ty.0, ty.1);
1451                // A resource created outside any target element is one the map produces.
1452                if destination.is_none() && run.tree.ty(created).is_resource() {
1453                    run.produced.push(created);
1454                }
1455                Ok(Some(created))
1456            }
1457            "copy" => {
1458                let parameter = parameters
1459                    .first()
1460                    .ok_or_else(|| fail("copy needs a value".into()))?;
1461                let node = self.parameter_node(run, group, parameter, vars).await?;
1462                Ok(Some(run.tree.deep_copy(node)))
1463            }
1464            "truncate" => {
1465                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1466                let length = self.argument_text(run, group, parameters, 1, vars).await?;
1467                let length: usize = length
1468                    .trim()
1469                    .parse()
1470                    .map_err(|_| fail(format!("truncate needs a length, not `{length}`")))?;
1471                let truncated = text.chars().take(length).collect();
1472                Ok(Some(self.string(run, truncated, "string")))
1473            }
1474            "cast" => {
1475                let source = self
1476                    .parameter_node(
1477                        run,
1478                        group,
1479                        parameters
1480                            .first()
1481                            .ok_or_else(|| fail("cast needs a value".into()))?,
1482                        vars,
1483                    )
1484                    .await?;
1485                let type_name = if parameters.len() > 1 {
1486                    transforms::cast_type(
1487                        &self.argument_text(run, group, parameters, 1, vars).await?,
1488                    )
1489                } else {
1490                    // Without a type, the value takes the type of the element it is written to.
1491                    self.implied_type(run, group, destination, element, None, vars)
1492                        .ok()
1493                        .and_then(|(_, name)| name)
1494                        .ok_or_else(|| fail("cast needs a type: cast(value, 'type')".into()))?
1495                };
1496                if !run.tree.is_primitive(source) {
1497                    // A complex value casts only to its own type.
1498                    return match run.tree.fhir_type(source) {
1499                        Some(own) if own == type_name => Ok(Some(run.tree.deep_copy(source))),
1500                        own => Err(fail(format!(
1501                            "cannot cast a {} to {type_name}",
1502                            own.as_deref().unwrap_or("complex value")
1503                        ))),
1504                    };
1505                }
1506                let text = run.tree.text(source).unwrap_or_default();
1507                let value = transforms::cast(&text, &type_name).map_err(fail)?;
1508                let ty = types.structure(&type_url(&type_name, CORE));
1509                Ok(Some(run.tree.primitive(value, ty, Some(type_name))))
1510            }
1511            "append" => {
1512                let mut text = String::new();
1513                for index in 0..parameters.len() {
1514                    text.push_str(
1515                        &self
1516                            .argument_text(run, group, parameters, index, vars)
1517                            .await?,
1518                    );
1519                }
1520                Ok(Some(self.string(run, text, "string")))
1521            }
1522            "translate" => self.translate(run, group, rule, parameters, vars).await,
1523            "reference" | "pointer" => {
1524                let parameter = parameters
1525                    .first()
1526                    .ok_or_else(|| fail(format!("{transform} needs a resource")))?;
1527                let resource = self.parameter_node(run, group, parameter, vars).await?;
1528                let resource_type = run
1529                    .tree
1530                    .fhir_type(resource)
1531                    .filter(|_| {
1532                        run.tree.ty(resource).is_resource() || !run.tree.is_primitive(resource)
1533                    })
1534                    .ok_or_else(|| fail(format!("{transform} needs a resource")))?;
1535                let id = match run
1536                    .tree
1537                    .children(resource, "id")
1538                    .first()
1539                    .and_then(|&id| run.tree.text(id))
1540                {
1541                    Some(id) => id,
1542                    None => {
1543                        let id = self.ids.uuid();
1544                        run.tree
1545                            .set_text(resource, "id", &id, &types)
1546                            .map_err(fail)?;
1547                        id
1548                    }
1549                };
1550                Ok(Some(if transform == "reference" {
1551                    self.string(run, format!("{resource_type}/{id}"), "string")
1552                } else {
1553                    self.string(run, format!("urn:uuid:{id}"), "uri")
1554                }))
1555            }
1556            "uuid" => Ok(Some(self.string(run, self.ids.uuid(), "id"))),
1557            "toDateTime" | "toDate" | "toTime" => {
1558                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1559                let format = match parameters.get(1) {
1560                    Some(_) => Some(self.argument_text(run, group, parameters, 1, vars).await?),
1561                    None => None,
1562                };
1563                let (value, type_name) = match transform {
1564                    "toDateTime" => (datetime::to_date_time(&text, format.as_deref()), "dateTime"),
1565                    "toDate" => (datetime::to_date(&text, format.as_deref()), "date"),
1566                    _ => (datetime::to_time(&text, format.as_deref()), "time"),
1567                };
1568                Ok(Some(self.string(run, value.map_err(fail)?, type_name)))
1569            }
1570            "unixToDateTime" | "unixToDate" | "unixToTime" => {
1571                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1572                let millis: i64 = text.trim().parse().map_err(|_| {
1573                    fail(format!("`{text}` is not a Unix timestamp in milliseconds"))
1574                })?;
1575                let timezone = match parameters.get(1) {
1576                    Some(_) => Some(self.argument_text(run, group, parameters, 1, vars).await?),
1577                    None => None,
1578                };
1579                let (value, type_name) = match transform {
1580                    "unixToDateTime" => (
1581                        datetime::unix_to_date_time(millis, timezone.as_deref()),
1582                        "dateTime",
1583                    ),
1584                    "unixToDate" => (datetime::unix_to_date(millis, timezone.as_deref()), "date"),
1585                    _ => (datetime::unix_to_time(millis, timezone.as_deref()), "time"),
1586                };
1587                Ok(Some(self.string(run, value.map_err(fail)?, type_name)))
1588            }
1589            "escape" => {
1590                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1591                let (from, to) = match parameters.len() {
1592                    3 => (
1593                        self.argument_text(run, group, parameters, 1, vars).await?,
1594                        self.argument_text(run, group, parameters, 2, vars).await?,
1595                    ),
1596                    _ => (
1597                        "none".to_owned(),
1598                        self.argument_text(run, group, parameters, 1, vars).await?,
1599                    ),
1600                };
1601                let escaped = datetime::escape(&text, &from, &to).map_err(fail)?;
1602                Ok(Some(self.string(run, escaped, "string")))
1603            }
1604            "cc" | "c" | "id" | "cp" | "qty" => {
1605                let mut arguments = Vec::with_capacity(parameters.len());
1606                for index in 0..parameters.len() {
1607                    arguments.push(
1608                        self.argument_text(run, group, parameters, index, vars)
1609                            .await?,
1610                    );
1611                }
1612                let (json, type_name) = match transform {
1613                    "cc" => (transforms::codeable_concept(&arguments), "CodeableConcept"),
1614                    "c" => match arguments.as_slice() {
1615                        [system, code, rest @ ..] => {
1616                            (transforms::coding(system, code, rest.first()), "Coding")
1617                        }
1618                        _ => return Err(fail("c needs a system and a code".into())),
1619                    },
1620                    "id" => (transforms::identifier(&arguments), "Identifier"),
1621                    "cp" => (transforms::contact_point(&arguments), "ContactPoint"),
1622                    _ => (transforms::quantity(&arguments).map_err(fail)?, "Quantity"),
1623                };
1624                let context = destination
1625                    .and_then(|d| run.tree.ty(d).structure().map(|s| s.url.clone()))
1626                    .unwrap_or_else(|| CORE.to_owned());
1627                let ty = types.structure(&type_url(type_name, &context));
1628                let node = run.tree.load(&json, ty, &types);
1629                Ok(Some(node))
1630            }
1631            "dateOp" => Err(fail(DATE_OP_UNDEFINED.into())),
1632            other => Err(fail(format!("the {other} transform is not supported"))),
1633        }
1634    }
1635
1636    /// The type `create()` makes without a type argument: the element's only type, or the
1637    /// source's type.
1638    fn implied_type(
1639        &self,
1640        run: &Run,
1641        group: &GroupRef,
1642        destination: Option<NodeId>,
1643        element: Option<&str>,
1644        source_variable: Option<&str>,
1645        vars: &Vars,
1646    ) -> std::result::Result<(Type, Option<String>), String> {
1647        let types = self.types();
1648        if let (Some(dest), Some(element)) = (destination, element)
1649            && let Lookup::Found(found) = types.lookup(run.tree.ty(dest), element)
1650            && let [single] = found.element.types.as_slice()
1651            && single != "Resource"
1652            && single != "*"
1653        {
1654            return Ok((types.element_type(&found, None), Some(single.clone())));
1655        }
1656        if let Some(source) = source_variable.and_then(|s| vars.get(Mode::Input, s)) {
1657            // The type the default group for the source's type produces, as the reference engine
1658            // does: a contained R4 Medication becomes the R5 Medication its group maps to.
1659            if let Some(source_type) = run.tree.fhir_type(source)
1660                && let Some(produced) = self.default_target_type(group, &source_type)
1661            {
1662                return Ok(produced);
1663            }
1664            return Ok((run.tree.ty(source).clone(), run.tree.fhir_type(source)));
1665        }
1666        Err("cannot tell what type to create: name it, as in create('Type')".into())
1667    }
1668
1669    /// The target type of the `<<type+>>` group whose source is `source_type`, in the map or the
1670    /// maps it imports.
1671    fn default_target_type(&self, group: &GroupRef, source_type: &str) -> Option<TypedTarget> {
1672        if let Some(found) = self.default_target_in(&group.map, source_type) {
1673            return Some(found);
1674        }
1675        let key = (imports_key(&group.map), source_type.to_owned());
1676        if let Some(found) = self.cache().default_target.get(&key) {
1677            return found.clone();
1678        }
1679        let found = self.find_default_target_type(group, source_type);
1680        self.cache().default_target.insert(key, found.clone());
1681        found
1682    }
1683
1684    /// The target type of a `<<type+>>` group of `map` itself whose source is `source_type`.
1685    fn default_target_in(&self, map: &StructureMap, source_type: &str) -> Option<TypedTarget> {
1686        map.group.iter().find_map(|candidate| {
1687            if candidate.type_mode.as_deref() != Some("type-and-types") {
1688                return None;
1689            }
1690            let [source, target] = candidate.input.as_slice() else {
1691                return None;
1692            };
1693            if source.mode != "source"
1694                || target.mode != "target"
1695                || !self.stated_type_is(map, source.type_name.as_deref(), source_type)
1696            {
1697                return None;
1698            }
1699            let stated = target.type_name.as_deref()?;
1700            let ty = self
1701                .types()
1702                .structure(&self.type_name_url(map, stated, CORE));
1703            let name = ty.fhir_type().unwrap_or_else(|| stated.to_owned());
1704            Some((ty, Some(name)))
1705        })
1706    }
1707
1708    fn find_default_target_type(&self, group: &GroupRef, source_type: &str) -> Option<TypedTarget> {
1709        group.map.import.iter().find_map(|import| {
1710            self.maps
1711                .resolve(import)
1712                .iter()
1713                .filter(|imported| imported.url != group.map.url)
1714                .find_map(|imported| self.default_target_in(imported, source_type))
1715        })
1716    }
1717
1718    /// `translate(value, map, output)`: a code, Coding or CodeableConcept translated with a
1719    /// ConceptMap contained in the map (`'#id'`) or found by the ConceptMap resolver.
1720    async fn translate(
1721        &self,
1722        run: &mut Run,
1723        group: &GroupRef,
1724        rule: &Rule,
1725        parameters: &[Parameter],
1726        vars: &Vars,
1727    ) -> Result<Option<NodeId>> {
1728        let fail = |message: String| Error::in_rule(rule, message);
1729        let source = self
1730            .parameter_node(
1731                run,
1732                group,
1733                parameters
1734                    .first()
1735                    .ok_or_else(|| fail("translate needs a value".into()))?,
1736                vars,
1737            )
1738            .await?;
1739        let map_url = self.argument_text(run, group, parameters, 1, vars).await?;
1740        let output = self.argument_text(run, group, parameters, 2, vars).await?;
1741        let concept_map = match map_url.strip_prefix('#') {
1742            Some(id) => Some(
1743                group
1744                    .map
1745                    .contained
1746                    .iter()
1747                    .find(|r| r["resourceType"] == "ConceptMap" && r["id"] == id)
1748                    .cloned()
1749                    .map(Arc::new)
1750                    .ok_or_else(|| fail(format!("no contained ConceptMap `{id}`")))?,
1751            ),
1752            None => self.concept_maps.resolve(&map_url),
1753        };
1754        if concept_map.is_none() && self.translator.is_none() {
1755            return Err(fail(format!("ConceptMap `{map_url}` not found")));
1756        }
1757        let codes = self.codes_of(run, source);
1758        let mut translation = None;
1759        let mut reported = false;
1760        for (system, code) in &codes {
1761            let found = match (&concept_map, self.translator) {
1762                (Some(concept_map), _) => translate(concept_map, system.as_deref(), code),
1763                (None, Some(translator)) => {
1764                    translator
1765                        .translate(&map_url, system.as_deref(), code)
1766                        .await
1767                }
1768                (None, None) => unreachable!("checked above"),
1769            };
1770            let found = match found {
1771                Ok(found) => found,
1772                // A code the ConceptMap marks as having no counterpart: left out, like a code it
1773                // does not know, when the host asked for that.
1774                Err(e) if self.lenient_translations => {
1775                    let rule_name = rule.name.as_deref().unwrap_or("?");
1776                    run.warnings
1777                        .push(format!("rule `{rule_name}`: {e} in `{map_url}`; left out"));
1778                    reported = true;
1779                    None
1780                }
1781                Err(e) => return Err(fail(format!("{e} in `{map_url}`"))),
1782            };
1783            if let Some(found) = found {
1784                translation = Some(found);
1785                break;
1786            }
1787        }
1788        let Some(translation) = translation else {
1789            if reported {
1790                return Ok(None);
1791            }
1792            let shown: Vec<&str> = codes.iter().map(|(_, c)| c.as_str()).collect();
1793            let rule_name = rule.name.as_deref().unwrap_or("?");
1794            run.warnings.push(format!(
1795                "rule `{rule_name}`: `{}` has no translation in `{map_url}`; left out",
1796                shown.join(", ")
1797            ));
1798            return Ok(None);
1799        };
1800
1801        let types = self.types();
1802        let mut coding = serde_json::Map::new();
1803        if let Some(system) = &translation.system {
1804            coding.insert("system".into(), system.clone().into());
1805        }
1806        if let Some(code) = &translation.code {
1807            coding.insert("code".into(), code.clone().into());
1808        }
1809        if let Some(display) = &translation.display {
1810            coding.insert("display".into(), display.clone().into());
1811        }
1812        let text = |value: &Option<String>| value.clone().map_or(Value::Null, Value::String);
1813        let (json, type_name) = match output.as_str() {
1814            "code" => (text(&translation.code), "code"),
1815            "system" => (text(&translation.system), "uri"),
1816            "display" => (text(&translation.display), "string"),
1817            "Coding" => (Value::Object(coding), "Coding"),
1818            "CodeableConcept" => (
1819                json!({ "coding": [Value::Object(coding)] }),
1820                "CodeableConcept",
1821            ),
1822            other => return Err(fail(format!("unknown translate output `{other}`"))),
1823        };
1824        let ty = types.structure(&type_url(type_name, CORE));
1825        Ok(Some(if json.is_object() {
1826            run.tree.load(&json, ty, &types)
1827        } else {
1828            run.tree.primitive(json, ty, Some(type_name.to_owned()))
1829        }))
1830    }
1831
1832    /// The (system, code) pairs a value holds: a code, a Coding, or a CodeableConcept's codings.
1833    fn codes_of(&self, run: &Run, node: NodeId) -> Vec<(Option<String>, String)> {
1834        let text = |id: NodeId, name: &str| {
1835            run.tree
1836                .children(id, name)
1837                .first()
1838                .and_then(|&child| run.tree.text(child))
1839        };
1840        if run.tree.is_primitive(node) {
1841            return run
1842                .tree
1843                .text(node)
1844                .map(|code| (None, code))
1845                .into_iter()
1846                .collect();
1847        }
1848        let codings = run.tree.children(node, "coding");
1849        let codings = if codings.is_empty() {
1850            vec![node]
1851        } else {
1852            codings
1853        };
1854        codings
1855            .into_iter()
1856            .filter_map(|coding| Some((text(coding, "system"), text(coding, "code")?)))
1857            .collect()
1858    }
1859
1860    // ---- Parameters -------------------------------------------------------------------------
1861
1862    /// A parameter as a node: a variable (or constant), or a new primitive for a literal.
1863    async fn parameter_node(
1864        &self,
1865        run: &mut Run,
1866        group: &GroupRef,
1867        parameter: &Parameter,
1868        vars: &Vars,
1869    ) -> Result<NodeId> {
1870        let types = self.types();
1871        let (value, type_name) = match &parameter.value {
1872            ParameterValue::Id(name) => return self.variable(run, group, name, vars).await,
1873            ParameterValue::String(v) => (Value::String(v.clone()), "string"),
1874            ParameterValue::Boolean(v) => (Value::Bool(*v), "boolean"),
1875            ParameterValue::Integer(v) => (Value::from(*v), "integer"),
1876            ParameterValue::Decimal(v) => (Value::Number(v.clone()), "decimal"),
1877            ParameterValue::Date(v) => (Value::String(v.clone()), "date"),
1878            ParameterValue::Time(v) => (Value::String(v.clone()), "time"),
1879            ParameterValue::DateTime(v) => (Value::String(v.clone()), "dateTime"),
1880        };
1881        let ty = types.structure(&type_url(type_name, CORE));
1882        Ok(run.tree.primitive(value, ty, Some(type_name.to_owned())))
1883    }
1884
1885    /// A variable, or failing that a constant of the map.
1886    async fn variable(
1887        &self,
1888        run: &mut Run,
1889        group: &GroupRef,
1890        name: &str,
1891        vars: &Vars,
1892    ) -> Result<NodeId> {
1893        if let Some(node) = vars.value(name) {
1894            return Ok(node);
1895        }
1896        // A dotted id such as `src.code` is a path from a variable.
1897        if let Some((variable, path)) = name.split_once('.')
1898            && let Some(mut node) = vars.value(variable)
1899        {
1900            for segment in path.split('.') {
1901                node = *run
1902                    .tree
1903                    .children(node, segment)
1904                    .first()
1905                    .ok_or_else(|| Error::new(format!("`{name}` has no value")))?;
1906            }
1907            return Ok(node);
1908        }
1909        let values = self.constant(run, group, name).await?;
1910        match values.as_slice() {
1911            [single] => Ok(*single),
1912            [] => Err(Error::new(format!("unknown variable `{name}`"))),
1913            _ => Err(Error::new(format!("constant `{name}` has several values"))),
1914        }
1915    }
1916
1917    async fn parameter_text(
1918        &self,
1919        run: &mut Run,
1920        group: &GroupRef,
1921        parameter: &Parameter,
1922        vars: &Vars,
1923    ) -> Result<String> {
1924        if let ParameterValue::String(text) = &parameter.value {
1925            return Ok(text.clone());
1926        }
1927        let node = self.parameter_node(run, group, parameter, vars).await?;
1928        Ok(run.tree.text(node).unwrap_or_default())
1929    }
1930
1931    async fn argument_text(
1932        &self,
1933        run: &mut Run,
1934        group: &GroupRef,
1935        parameters: &[Parameter],
1936        index: usize,
1937        vars: &Vars,
1938    ) -> Result<String> {
1939        let parameter = parameters
1940            .get(index)
1941            .ok_or_else(|| Error::new(format!("missing argument {}", index + 1)))?;
1942        self.parameter_text(run, group, parameter, vars).await
1943    }
1944
1945    fn literal(&self, run: &mut Run, value: Value, type_name: &str) -> NodeId {
1946        let ty = self.types().structure(&type_url(type_name, CORE));
1947        run.tree.primitive(value, ty, Some(type_name.to_owned()))
1948    }
1949
1950    fn string(&self, run: &mut Run, text: String, type_name: &str) -> NodeId {
1951        let ty = self.types().structure(&type_url(type_name, CORE));
1952        run.tree
1953            .primitive(Value::String(text), ty, Some(type_name.to_owned()))
1954    }
1955
1956    // ---- FHIRPath ---------------------------------------------------------------------------
1957
1958    /// The value of the map's `let name = …;`, evaluated once.
1959    fn constant<'r>(
1960        &'r self,
1961        run: &'r mut Run,
1962        group: &'r GroupRef,
1963        name: &'r str,
1964    ) -> BoxFuture<'r, Result<Vec<NodeId>>> {
1965        Box::pin(async move {
1966            let key = (group.map.url.clone().unwrap_or_default(), name.to_owned());
1967            if let Some(values) = run.constants.get(&key) {
1968                return Ok(values.clone());
1969            }
1970            let Some(constant) = group.map.constants.iter().find(|c| c.name == name) else {
1971                return Ok(Vec::new());
1972            };
1973            if !run.evaluating.insert(key.clone()) {
1974                return Err(Error::new(format!("constant `{name}` refers to itself")));
1975            }
1976            let values = self
1977                .evaluate(run, group, &constant.value, None, &Vars::default())
1978                .await;
1979            run.evaluating.remove(&key);
1980            let values = values?;
1981            run.constants.insert(key, values.clone());
1982            Ok(values)
1983        })
1984    }
1985
1986    fn evaluate<'r>(
1987        &'r self,
1988        run: &'r mut Run,
1989        group: &'r GroupRef,
1990        expression: &'r str,
1991        focus: Option<NodeId>,
1992        vars: &'r Vars,
1993    ) -> BoxFuture<'r, Result<Vec<NodeId>>> {
1994        Box::pin(async move {
1995            // Only the variables the expression names: serializing every variable in scope for
1996            // every expression would copy the whole input each time.
1997            let referenced = path_start_names(expression);
1998            let mut names: HashSet<String> = vars
1999                .names()
2000                .filter(|name| referenced.contains(*name))
2001                .map(str::to_owned)
2002                .collect();
2003            let mut variables = Vec::new();
2004            for name in &names {
2005                if let Some(node) = vars.value(name) {
2006                    variables.push((name.clone(), self.item(run, node)));
2007                }
2008            }
2009            for constant in &group.map.constants {
2010                if names.contains(&constant.name) || expression == constant.value {
2011                    continue;
2012                }
2013                if referenced.contains(&constant.name) {
2014                    let values = self.constant(run, group, &constant.name).await?;
2015                    if let [value] = values.as_slice() {
2016                        variables.push((constant.name.clone(), self.item(run, *value)));
2017                        names.insert(constant.name.clone());
2018                    }
2019                }
2020            }
2021            let bound = bind_variables(expression, &names);
2022            let focus = focus.map(|node| self.item(run, node));
2023            let results = self
2024                .expressions
2025                .evaluate(&bound, focus.as_ref(), &variables)
2026                .await
2027                .map_err(|e| Error::new(format!("`{expression}`: {e}")))?;
2028            let types = self.types();
2029            Ok(results
2030                .into_iter()
2031                .map(|item| {
2032                    let ty = item
2033                        .type_name
2034                        .as_deref()
2035                        .map(|t| types.structure(&type_url(t, CORE)))
2036                        .unwrap_or(Type::Unknown);
2037                    if item.json.is_object() {
2038                        run.tree.load(&item.json, ty, &types)
2039                    } else {
2040                        run.tree.primitive(item.json, ty, item.type_name)
2041                    }
2042                })
2043                .collect())
2044        })
2045    }
2046
2047    async fn evaluate_boolean(
2048        &self,
2049        run: &mut Run,
2050        group: &GroupRef,
2051        expression: &str,
2052        focus: Option<NodeId>,
2053        vars: &Vars,
2054    ) -> Result<bool> {
2055        let values = self.evaluate(run, group, expression, focus, vars).await?;
2056        Ok(match values.as_slice() {
2057            [] => false,
2058            [single] => run.tree.value(*single) != Some(&Value::Bool(false)),
2059            _ => true,
2060        })
2061    }
2062
2063    /// A node for FHIRPath. A backbone element is named by its path (`QuestionnaireResponse.item`),
2064    /// as FHIRPath models it, so its elements keep their FHIR types.
2065    fn item(&self, run: &Run, node: NodeId) -> Item {
2066        let type_name = match run.tree.ty(node) {
2067            backbone @ Type::Node { path, .. } if !path.is_empty() => Some(backbone.display()),
2068            _ => run.tree.fhir_type(node),
2069        };
2070        Item {
2071            json: run.tree.to_json(node),
2072            type_name,
2073        }
2074    }
2075}
2076
2077/// The element path of a source or target: the element and the segments in the element
2078/// extension.
2079fn element_path(
2080    element: Option<&str>,
2081    extensions: &[fml_model::model::Extension],
2082    url: &str,
2083) -> Vec<String> {
2084    element
2085        .map(str::to_owned)
2086        .into_iter()
2087        .chain(
2088            extensions
2089                .iter()
2090                .filter(|e| e.url == url)
2091                .filter_map(|e| e.value_string.clone()),
2092        )
2093        .collect()
2094}
2095
2096/// Target list modes (the `StructureMapTargetListMode` codes; the IG leaves them to be
2097/// documented): a `first` rule's values go before the others in a repeating element and a `last`
2098/// rule's after them, one rule each per element; `single` allows one value per element.
2099fn order_target(
2100    run: &mut Run,
2101    rule: &Rule,
2102    target: &Target,
2103    dest: NodeId,
2104    element: &str,
2105    value: NodeId,
2106) -> Result<()> {
2107    let identity = std::ptr::from_ref(target) as usize;
2108    let mode = target
2109        .list_mode
2110        .iter()
2111        .find(|mode| matches!(mode.as_str(), "first" | "last" | "single"))
2112        .map(String::as_str);
2113    if mode == Some("single") && !run.single.insert((dest, element.to_owned(), identity)) {
2114        return Err(Error::in_rule(
2115            rule,
2116            format!("target `{element}` is `single`, but the rule makes more than one"),
2117        ));
2118    }
2119    let key = (dest, element.to_owned());
2120    let order = match (mode, run.order.contains_key(&key)) {
2121        (Some("first" | "last"), _) | (_, true) => run.order.entry(key).or_default(),
2122        _ => return Ok(()),
2123    };
2124    match mode {
2125        Some("first") => {
2126            if order.first.is_some_and(|first| first != identity) {
2127                return Err(Error::in_rule(
2128                    rule,
2129                    format!("two rules put their values `first` in `{element}`"),
2130                ));
2131            }
2132            order.first = Some(identity);
2133            let at = order.first_count;
2134            order.first_count += 1;
2135            run.tree.move_value(dest, element, value, Some(at));
2136        }
2137        Some("last") => {
2138            if order.last.is_some_and(|last| last != identity) {
2139                return Err(Error::in_rule(
2140                    rule,
2141                    format!("two rules put their values `last` in `{element}`"),
2142                ));
2143            }
2144            order.last = Some(identity);
2145            order.last_values.push(value);
2146        }
2147        _ => {}
2148    }
2149    let last_values = order.last_values.clone();
2150    for last in last_values {
2151        run.tree.move_value(dest, element, last, None);
2152    }
2153    Ok(())
2154}
2155
2156/// A source's `min..max`: the element must have that many values (fml.md, "Source Content").
2157fn check_cardinality(rule: &Rule, source: &Source, count: usize) -> Result<()> {
2158    let below = source
2159        .min
2160        .is_some_and(|min| i64::try_from(count).is_ok_and(|count| count < min));
2161    let above = source
2162        .max
2163        .as_deref()
2164        .and_then(|max| max.parse::<usize>().ok())
2165        .is_some_and(|max| count > max);
2166    if below || above {
2167        let path = std::iter::once(source.context.as_str())
2168            .chain(source.element.as_deref())
2169            .collect::<Vec<_>>()
2170            .join(".");
2171        return Err(Error::in_rule(
2172            rule,
2173            format!(
2174                "`{path}` has {count} values, outside {}..{}",
2175                source.min.unwrap_or(0),
2176                source.max.as_deref().unwrap_or("*")
2177            ),
2178        ));
2179    }
2180    Ok(())
2181}
2182
2183/// The key of a map's imports in the group cache: its URL and what it imports.
2184fn imports_key(map: &StructureMap) -> String {
2185    let mut key = map.url.clone().unwrap_or_default();
2186    for import in &map.import {
2187        key.push('\n');
2188        key.push_str(import);
2189    }
2190    key
2191}
2192
2193/// A dependent group argument's variable name.
2194fn parameter_name(parameter: &Parameter) -> String {
2195    match &parameter.value {
2196        ParameterValue::Id(name) | ParameterValue::String(name) => name.clone(),
2197        other => format!("{other:?}"),
2198    }
2199}
2200
2201/// The variables of a rule that copies one value to one target with nothing else to do.
2202fn simple_copy(rule: &Rule) -> Option<(&str, &str)> {
2203    let ([source], [target]) = (rule.source.as_slice(), rule.target.as_slice()) else {
2204        return None;
2205    };
2206    (target.transform.as_deref() == Some("create") && target.parameter.is_empty()).then_some(())?;
2207    Some((source.variable.as_deref()?, target.variable.as_deref()?))
2208}