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