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        if run.trace.is_some() {
445            // The trace says where an input variable is in the input (debug.md, variable.path).
446            let root_name = run
447                .tree
448                .fhir_type(source_node)
449                .unwrap_or_else(|| "%resource".to_owned());
450            run.tree.record_paths(source_node, &root_name);
451        }
452        let target_type = self.input_type(&map, target_input.type_name.as_deref());
453        let target_node = match target {
454            Some(target) => run.tree.load(target, target_type, &types),
455            None => {
456                if matches!(target_type, Type::Unknown) {
457                    return Err(Error::new(format!(
458                        "cannot create the target: type `{}` is not known",
459                        target_input.type_name.as_deref().unwrap_or("?")
460                    )));
461                }
462                run.tree.create(target_type, None)
463            }
464        };
465        let mut vars = Vars::default();
466        vars.add(Mode::Input, &source_input.name, source_node);
467        vars.add(Mode::Output, &target_input.name, target_node);
468        let entry = GroupRef {
469            map: map.clone(),
470            index: 0,
471        };
472        if let Err(mut error) = self.execute_group(&mut run, entry, vars, true).await {
473            let truncated = run.trace_truncated.take();
474            error.trace = run.trace.take().map(|events| {
475                Box::new(Trace {
476                    status: match truncated {
477                        Some(_) => TraceStatus::Truncated,
478                        None => TraceStatus::Stopped,
479                    },
480                    note: truncated,
481                    events,
482                })
483            });
484            return Err(error);
485        }
486        let in_target = run.tree.reachable(target_node);
487        let produced = run
488            .produced
489            .iter()
490            .filter(|node| !in_target.contains(node))
491            .map(|&node| run.tree.to_json(node))
492            .collect();
493        Ok(Transformed {
494            target: run.tree.to_json(target_node),
495            log: run.log,
496            warnings: run.warnings,
497            produced,
498            trace: run.trace.map(|events| Trace {
499                status: match run.trace_truncated {
500                    Some(_) => TraceStatus::Truncated,
501                    None => TraceStatus::Complete,
502                },
503                note: run.trace_truncated,
504                events,
505            }),
506        })
507    }
508
509    /// The type a group parameter or `create` names: a `uses` alias, a URL, or a type name.
510    fn input_type(&self, map: &StructureMap, name: Option<&str>) -> Type {
511        let Some(name) = name else {
512            return Type::Unknown;
513        };
514        self.types().structure(&self.type_name_url(map, name, CORE))
515    }
516
517    fn type_name_url(&self, map: &StructureMap, name: &str, context: &str) -> String {
518        if let Some(structure) = map
519            .structure
520            .iter()
521            .find(|s| s.alias.as_deref() == Some(name))
522        {
523            return structure.url.clone();
524        }
525        type_url(name, context)
526    }
527
528    // ---- Groups -----------------------------------------------------------------------------
529
530    fn execute_group<'r>(
531        &'r self,
532        run: &'r mut Run,
533        group: GroupRef,
534        vars: Vars,
535        at_root: bool,
536    ) -> BoxFuture<'r, Result<()>> {
537        Box::pin(async move {
538            run.depth += 1;
539            if run.depth > self.limits.max_depth {
540                return Err(Error::limit(format!(
541                    "groups nest more than {} deep; is a group calling itself without end?",
542                    self.limits.max_depth
543                )));
544            }
545            let map = group.map.clone();
546            let definition = group.group();
547            let position = format!("StructureMap.group[{}]", group.index);
548            self.record(run, Phase::Enter, &map, &position, &vars, Vec::new());
549            if let Some(extends) = &definition.extends {
550                let base = self.group_by_name(&map, extends)?;
551                self.execute_group(run, base, vars.clone(), false).await?;
552            }
553            let mut vars = vars;
554            for (index, rule) in definition.rule.iter().enumerate() {
555                let rule_position = format!("{position}.rule[{index}]");
556                self.execute_rule(run, &group, rule, &rule_position, &mut vars, at_root)
557                    .await?;
558            }
559            self.record(run, Phase::Exit, &map, &position, &vars, Vec::new());
560            run.depth -= 1;
561            Ok(())
562        })
563    }
564
565    /// The group `name`, in the map or the maps it imports.
566    fn group_by_name(&self, map: &Arc<StructureMap>, name: &str) -> Result<GroupRef> {
567        if let Some(index) = map.group.iter().position(|g| g.name == name) {
568            return Ok(GroupRef {
569                map: map.clone(),
570                index,
571            });
572        }
573        let key = (imports_key(map), name.to_owned());
574        let cached = self.cache().by_name.get(&key).cloned();
575        let found = match cached {
576            Some(found) => found,
577            None => {
578                let found = self.find_imported_group_by_name(map, name);
579                self.cache().by_name.insert(key, found.clone());
580                found
581            }
582        };
583        match found {
584            Found::One(group) => Ok(group),
585            Found::None => Err(Error::new(format!(
586                "no group `{name}` in {} or the maps it imports",
587                map.url.as_deref().unwrap_or("the map")
588            ))),
589            Found::Several(urls) => Err(Error::new(format!(
590                "group `{name}` is in several imported maps ({}); a group name must be unique",
591                urls.join(", ")
592            ))),
593        }
594    }
595
596    /// The group in exactly one of the maps `map` imports (fml.md: a group name must be unique
597    /// among the imported maps).
598    fn find_imported_group_by_name(&self, map: &Arc<StructureMap>, name: &str) -> Found {
599        let mut found: Vec<GroupRef> = Vec::new();
600        for import in &map.import {
601            for imported in self.maps.resolve(import) {
602                if imported.url == map.url || found.iter().any(|g| g.map.url == imported.url) {
603                    continue;
604                }
605                if let Some(index) = imported.group.iter().position(|g| g.name == name) {
606                    found.push(GroupRef {
607                        map: imported.clone(),
608                        index,
609                    });
610                }
611            }
612        }
613        match found.len() {
614            0 => Found::None,
615            1 => Found::One(found.remove(0)),
616            _ => Found::Several(
617                found
618                    .iter()
619                    .map(|g| g.map.url.clone().unwrap_or_default())
620                    .collect(),
621            ),
622        }
623    }
624
625    /// The default group for a source and target type: one with a type mode whose parameter
626    /// types match.
627    fn group_by_types(
628        &self,
629        map: &Arc<StructureMap>,
630        source: Option<&str>,
631        target: Option<&str>,
632    ) -> Option<GroupRef> {
633        let (source, target) = (source?, target?);
634        if let Some(index) = self.default_group_index(map, source, target) {
635            return Some(GroupRef {
636                map: map.clone(),
637                index,
638            });
639        }
640        let key = (imports_key(map), source.to_owned(), target.to_owned());
641        if let Some(found) = self.cache().by_types.get(&key) {
642            return found.clone();
643        }
644        let found = self.find_imported_group_by_types(map, source, target);
645        self.cache().by_types.insert(key, found.clone());
646        found
647    }
648
649    /// The default group of `map` itself for the types.
650    fn default_group_index(&self, map: &StructureMap, source: &str, target: &str) -> Option<usize> {
651        map.group.iter().position(|group| {
652            group.type_mode.is_some()
653                && matches!(group.input.as_slice(), [s, t]
654                    if s.mode == "source" && t.mode == "target"
655                    && self.stated_type_is(map, s.type_name.as_deref(), source)
656                    && self.stated_type_is(map, t.type_name.as_deref(), target))
657        })
658    }
659
660    fn find_imported_group_by_types(
661        &self,
662        map: &Arc<StructureMap>,
663        source: &str,
664        target: &str,
665    ) -> Option<GroupRef> {
666        for import in &map.import {
667            for imported in self.maps.resolve(import) {
668                if imported.url == map.url {
669                    continue;
670                }
671                if let Some(index) = self.default_group_index(&imported, source, target) {
672                    return Some(GroupRef {
673                        map: imported,
674                        index,
675                    });
676                }
677            }
678        }
679        None
680    }
681
682    /// The type a source type filter names: through a `uses` alias to the structure's type
683    /// (`: CodeableConceptR4` is `CodeableConcept`), or the name as written.
684    fn stated_type_name(&self, map: &StructureMap, stated: &str) -> String {
685        map.structure
686            .iter()
687            .find(|s| s.alias.as_deref() == Some(stated))
688            .and_then(|s| self.definitions.structure(&s.url))
689            .map_or_else(|| stated.to_owned(), |s| s.type_name.clone())
690    }
691
692    fn stated_type_is(&self, map: &StructureMap, stated: Option<&str>, actual: &str) -> bool {
693        let Some(stated) = stated else { return false };
694        if stated == actual {
695            return true;
696        }
697        let url = self.type_name_url(map, stated, CORE);
698        self.definitions
699            .structure(&url)
700            .is_some_and(|s| s.type_name == actual)
701    }
702
703    // ---- Rules ------------------------------------------------------------------------------
704
705    fn execute_rule<'r>(
706        &'r self,
707        run: &'r mut Run,
708        group: &'r GroupRef,
709        rule: &'r Rule,
710        position: &'r str,
711        vars: &'r mut Vars,
712        at_root: bool,
713    ) -> BoxFuture<'r, Result<()>> {
714        Box::pin(async move {
715            self.check_limits(run)?;
716            self.record(run, Phase::Enter, &group.map, position, vars, Vec::new());
717            let mut bindings = vec![vars.clone()];
718            for (index, source) in rule.source.iter().enumerate() {
719                let logged = run.log.len();
720                let mut next = Vec::new();
721                for binding in &bindings {
722                    next.extend(
723                        self.process_source(run, group, rule, source, binding)
724                            .await
725                            .map_err(|e| {
726                                e.at(&group.map, &format!("{position}.source[{index}]"))
727                            })?,
728                    );
729                }
730                bindings = next;
731                if run.trace.is_some() {
732                    let messages = run.log[logged..].to_vec();
733                    let shown = bindings.first().cloned().unwrap_or_else(|| vars.clone());
734                    let step = format!("{position}.source[{index}]");
735                    self.record(run, Phase::Step, &group.map, &step, &shown, messages);
736                }
737            }
738            let source_variable = match rule.source.as_slice() {
739                [single] => single.variable.as_deref(),
740                _ => None,
741            };
742            for mut binding in bindings {
743                for (index, target) in rule.target.iter().enumerate() {
744                    self.process_target(
745                        run,
746                        group,
747                        rule,
748                        target,
749                        &mut binding,
750                        vars,
751                        source_variable,
752                        at_root,
753                    )
754                    .await
755                    .map_err(|e| e.at(&group.map, &format!("{position}.target[{index}]")))?;
756                    if run.trace.is_some() {
757                        let step = format!("{position}.target[{index}]");
758                        self.record(run, Phase::Step, &group.map, &step, &binding, Vec::new());
759                    }
760                }
761                // The named groups run first, then the nested rules (fml.md, "Dependent Rules").
762                for dependent in &rule.dependent {
763                    self.execute_dependency(run, group, rule, dependent, &binding)
764                        .await
765                        .map_err(|e| e.at(&group.map, position))?;
766                }
767                for (index, child) in rule.rule.iter().enumerate() {
768                    let child_position = format!("{position}.rule[{index}]");
769                    self.execute_rule(run, group, child, &child_position, &mut binding, false)
770                        .await?;
771                }
772                if rule.dependent.is_empty()
773                    && rule.rule.is_empty()
774                    && let Some((source, target)) = simple_copy(rule)
775                {
776                    self.default_copy(run, group, rule, source, target, &binding)
777                        .await
778                        .map_err(|e| e.at(&group.map, position))?;
779                }
780            }
781            self.record(run, Phase::Exit, &group.map, position, vars, Vec::new());
782            Ok(())
783        })
784    }
785
786    /// Stops the transform when the caller cancelled it or it ran past its limits.
787    fn check_limits(&self, run: &mut Run) -> Result<()> {
788        if self
789            .cancel
790            .is_some_and(|cancel| cancel.load(Ordering::Relaxed))
791        {
792            return Err(Error {
793                kind: ErrorKind::Cancelled,
794                ..Error::new("the transform was cancelled")
795            });
796        }
797        run.rules += 1;
798        if run.rules > self.limits.max_rules {
799            return Err(Error::limit(format!(
800                "the transform ran more than {} rules",
801                self.limits.max_rules
802            )));
803        }
804        if run.tree.len() > self.limits.max_values {
805            return Err(Error::limit(format!(
806                "the transform created more than {} values",
807                self.limits.max_values
808            )));
809        }
810        Ok(())
811    }
812
813    /// Records a trace event, when tracing.
814    fn record(
815        &self,
816        run: &mut Run,
817        phase: Phase,
818        map: &StructureMap,
819        position: &str,
820        vars: &Vars,
821        messages: Vec<String>,
822    ) {
823        if run.trace.is_none() {
824            return;
825        }
826        if phase == Phase::Exit {
827            run.trace_depth = run.trace_depth.saturating_sub(1);
828        }
829        if run.trace_truncated.is_some() {
830            if phase == Phase::Enter {
831                run.trace_depth += 1;
832            }
833            return;
834        }
835        let mut seen = HashSet::new();
836        let mut variables: Vec<TraceVariable> = vars
837            .entries
838            .iter()
839            .rev()
840            .filter(|(mode, name, _)| *mode != Mode::Shared && seen.insert(name.clone()))
841            .map(|(mode, name, node)| TraceVariable {
842                name: name.clone(),
843                context_type: match mode {
844                    Mode::Input => "input",
845                    _ => "output",
846                }
847                .to_owned(),
848                value: run.tree.to_json(*node),
849                data_type: self.item(run, *node).type_name,
850                path: run.tree.path(*node).map(str::to_owned),
851            })
852            .collect();
853        variables.reverse();
854        let event = TraceEvent {
855            phase,
856            depth: run.trace_depth,
857            source: map.url.clone(),
858            position: position.to_owned(),
859            variables,
860            messages,
861        };
862        if run.trace_truncated.is_none() {
863            let size = event.approximate_size();
864            let count = run.trace.as_ref().map_or(0, Vec::len);
865            if count >= self.limits.max_trace_events {
866                run.trace_truncated = Some(format!(
867                    "the trace keeps its first {} events",
868                    self.limits.max_trace_events
869                ));
870            } else if run.trace_bytes + size > self.limits.max_trace_bytes {
871                run.trace_truncated = Some(format!(
872                    "the trace keeps its first {count} events, about {} bytes",
873                    run.trace_bytes
874                ));
875            } else if let Some(events) = &mut run.trace {
876                run.trace_bytes += size;
877                events.push(event);
878            }
879        }
880        if phase == Phase::Enter {
881            run.trace_depth += 1;
882        }
883    }
884
885    /// `src.a as s -> tgt.a as t` with nothing else to do: the default group for the two types
886    /// maps one into the other, or the value is copied when the types are the same.
887    async fn default_copy(
888        &self,
889        run: &mut Run,
890        group: &GroupRef,
891        rule: &Rule,
892        source: &str,
893        target: &str,
894        vars: &Vars,
895    ) -> Result<()> {
896        let source = vars
897            .get(Mode::Input, source)
898            .ok_or_else(|| Error::in_rule(rule, format!("no source variable `{source}`")))?;
899        let target = vars
900            .get(Mode::Output, target)
901            .ok_or_else(|| Error::in_rule(rule, format!("no target variable `{target}`")))?;
902        let source_type = run.tree.fhir_type(source);
903        let target_type = run.tree.fhir_type(target);
904        if let Some(default) =
905            self.group_by_types(&group.map, source_type.as_deref(), target_type.as_deref())
906        {
907            let inputs = &default.group().input;
908            let mut call = Vars::default();
909            call.add(Mode::Input, &inputs[0].name, source);
910            call.add(Mode::Output, &inputs[1].name, target);
911            return self.execute_group(run, default, call, false).await;
912        }
913        if source_type == target_type || target_type.is_none() {
914            run.tree.assign(target, source);
915            return Ok(());
916        }
917        Err(Error::in_rule(
918            rule,
919            format!(
920                "no default group maps {} to {}",
921                source_type.as_deref().unwrap_or("?"),
922                target_type.as_deref().unwrap_or("?")
923            ),
924        ))
925    }
926
927    async fn execute_dependency(
928        &self,
929        run: &mut Run,
930        group: &GroupRef,
931        rule: &Rule,
932        dependent: &Dependent,
933        vars: &Vars,
934    ) -> Result<()> {
935        let callee = self.group_by_name(&group.map, &dependent.name)?;
936        let inputs = &callee.group().input;
937        if inputs.len() != dependent.parameter.len() {
938            return Err(Error::in_rule(
939                rule,
940                format!(
941                    "group `{}` takes {} arguments, the call passes {}",
942                    dependent.name,
943                    inputs.len(),
944                    dependent.parameter.len()
945                ),
946            ));
947        }
948        let mut call = Vars::default();
949        for (input, parameter) in inputs.iter().zip(&dependent.parameter) {
950            let name = parameter_name(parameter);
951            let (mode, node) = if input.mode == "source" {
952                (Mode::Input, vars.value(&name))
953            } else {
954                (Mode::Output, vars.get(Mode::Output, &name))
955            };
956            let node = node.ok_or_else(|| {
957                Error::in_rule(
958                    rule,
959                    format!(
960                        "`{}` passes `{name}` as {} `{}`, but it has no value",
961                        dependent.name, input.mode, input.name
962                    ),
963                )
964            })?;
965            call.add(mode, &input.name, node);
966        }
967        self.execute_group(run, callee, call, false).await
968    }
969
970    // ---- Sources ----------------------------------------------------------------------------
971
972    async fn process_source(
973        &self,
974        run: &mut Run,
975        group: &GroupRef,
976        rule: &Rule,
977        source: &Source,
978        vars: &Vars,
979    ) -> Result<Vec<Vars>> {
980        let context = vars.get(Mode::Input, &source.context).ok_or_else(|| {
981            Error::in_rule(
982                rule,
983                format!("unknown source variable `{}`", source.context),
984            )
985        })?;
986        let mut items = vec![context];
987        for element in element_path(
988            source.element.as_deref(),
989            &source.extension,
990            SOURCE_ELEMENT_EXTENSION,
991        ) {
992            items = items
993                .iter()
994                .flat_map(|&item| run.tree.children(item, &element))
995                .collect();
996        }
997        check_cardinality(rule, source, items.len())?;
998        // The type filter selects the exact type; the default stands in when nothing is left.
999        if let Some(type_name) = &source.type_name {
1000            let wanted = self.stated_type_name(&group.map, type_name);
1001            items.retain(|&item| run.tree.fhir_type(item).as_deref() == Some(wanted.as_str()));
1002        }
1003        if items.is_empty()
1004            && let Some(default) = &source.default_value
1005        {
1006            items = self.evaluate(run, group, default, None, vars).await?;
1007        }
1008        let with_item = |item: NodeId| {
1009            let mut bound = vars.clone();
1010            if let Some(variable) = &source.variable {
1011                bound.add(Mode::Input, variable, item);
1012            }
1013            bound
1014        };
1015        if let Some(condition) = &source.condition {
1016            let mut kept = Vec::with_capacity(items.len());
1017            for item in items {
1018                if self
1019                    .evaluate_boolean(run, group, condition, Some(item), &with_item(item))
1020                    .await?
1021                {
1022                    kept.push(item);
1023                }
1024            }
1025            items = kept;
1026        }
1027        if let Some(check) = &source.check {
1028            for &item in &items {
1029                if !self
1030                    .evaluate_boolean(run, group, check, Some(item), &with_item(item))
1031                    .await?
1032                {
1033                    return Err(Error::in_rule(rule, format!("check `{check}` failed")));
1034                }
1035            }
1036        }
1037        if let Some(log) = &source.log_message {
1038            let mut messages = Vec::new();
1039            for &item in &items {
1040                let values = self
1041                    .evaluate(run, group, log, Some(item), &with_item(item))
1042                    .await?;
1043                messages.extend(values.into_iter().filter_map(|v| run.tree.text(v)));
1044            }
1045            if !messages.is_empty() {
1046                run.log.push(messages.join(","));
1047            }
1048        }
1049        match source.list_mode.as_deref() {
1050            _ if items.is_empty() => {}
1051            Some("first") => items.truncate(1),
1052            Some("not_first") => {
1053                items.remove(0);
1054            }
1055            Some("last") => items = vec![*items.last().unwrap()],
1056            Some("not_last") => {
1057                items.pop();
1058            }
1059            Some("only_one") if items.len() > 1 => {
1060                return Err(Error::in_rule(
1061                    rule,
1062                    format!("`{}` has more than one item", source.context),
1063                ));
1064            }
1065            _ => {}
1066        }
1067        Ok(items.into_iter().map(with_item).collect())
1068    }
1069
1070    // ---- Targets ----------------------------------------------------------------------------
1071
1072    #[allow(clippy::too_many_arguments)]
1073    async fn process_target(
1074        &self,
1075        run: &mut Run,
1076        group: &GroupRef,
1077        rule: &Rule,
1078        target: &Target,
1079        vars: &mut Vars,
1080        shared: &mut Vars,
1081        source_variable: Option<&str>,
1082        at_root: bool,
1083    ) -> Result<()> {
1084        let types = self.types();
1085        let elements = element_path(
1086            target.element.as_deref(),
1087            &target.extension,
1088            TARGET_ELEMENT_EXTENSION,
1089        );
1090        let element = elements.last().cloned();
1091        let mut destination = match &target.context {
1092            Some(context) => Some(vars.get(Mode::Output, context).ok_or_else(|| {
1093                Error::in_rule(rule, format!("unknown target variable `{context}`"))
1094            })?),
1095            None => None,
1096        };
1097        let sharing = target.list_mode.iter().any(|m| m == "share");
1098        let mut value = match (&target.transform, sharing, &target.list_rule_id) {
1099            (None, true, Some(id)) => shared.get(Mode::Shared, id),
1100            _ => None,
1101        };
1102        if let Some(dest) = destination
1103            && value.is_none()
1104        {
1105            let mut dest = dest;
1106            for intermediate in elements.iter().take(elements.len().saturating_sub(1)) {
1107                dest = run
1108                    .tree
1109                    .make_property(dest, intermediate, &types)
1110                    .map_err(|e| Error::in_rule(rule, e))?;
1111            }
1112            destination = Some(dest);
1113        }
1114        if target.transform.is_some() {
1115            let produced = if target.transform.as_deref() == Some("evaluate") {
1116                let values = self
1117                    .evaluate_transform(run, group, rule, target, vars)
1118                    .await?;
1119                if values.len() > 1 {
1120                    self.check_several(
1121                        run,
1122                        rule,
1123                        target,
1124                        destination,
1125                        element.as_deref(),
1126                        values.len(),
1127                    )?;
1128                }
1129                values
1130            } else {
1131                self.run_transform(
1132                    run,
1133                    group,
1134                    rule,
1135                    target,
1136                    vars,
1137                    destination,
1138                    element.as_deref(),
1139                    source_variable,
1140                    at_root,
1141                )
1142                .await?
1143                .into_iter()
1144                .collect()
1145            };
1146            if let Some(dest) = destination {
1147                for &item in &produced {
1148                    match &element {
1149                        Some(element) => {
1150                            run.tree
1151                                .set_property(dest, element, item, &types)
1152                                .map_err(|e| Error::in_rule(rule, e))?;
1153                            order_target(run, rule, target, dest, element, item)?;
1154                        }
1155                        None => run.tree.assign(dest, item),
1156                    }
1157                }
1158            }
1159            value = produced.last().copied();
1160        } else if let (Some(dest), None) = (destination, value) {
1161            value = Some(match &element {
1162                Some(element) => {
1163                    let created = run
1164                        .tree
1165                        .make_property(dest, element, &types)
1166                        .map_err(|e| Error::in_rule(rule, e))?;
1167                    if let (true, Some(id)) = (sharing, &target.list_rule_id) {
1168                        // Siblings find it in the enclosing scope, nested rules in this one.
1169                        shared.add(Mode::Shared, id, created);
1170                        vars.add(Mode::Shared, id, created);
1171                    }
1172                    order_target(run, rule, target, dest, element, created)?;
1173                    created
1174                }
1175                None => dest,
1176            });
1177        }
1178        if let (Some(variable), Some(value)) = (&target.variable, value) {
1179            vars.add(Mode::Output, variable, value);
1180        }
1181        Ok(())
1182    }
1183
1184    /// `evaluate(context, expression)` or `(expression)`: every value the expression gives.
1185    async fn evaluate_transform(
1186        &self,
1187        run: &mut Run,
1188        group: &GroupRef,
1189        rule: &Rule,
1190        target: &Target,
1191        vars: &Vars,
1192    ) -> Result<Vec<NodeId>> {
1193        let parameters = &target.parameter;
1194        let expression = match parameters.last().map(|p| &p.value) {
1195            Some(ParameterValue::String(expression)) => expression,
1196            _ => return Err(Error::in_rule(rule, "evaluate needs an expression")),
1197        };
1198        let focus = match parameters.as_slice() {
1199            [context, _] => Some(self.parameter_node(run, group, context, vars).await?),
1200            _ => None,
1201        };
1202        self.evaluate(run, group, expression, focus, vars).await
1203    }
1204
1205    /// Several values from `evaluate` make one target each, which needs a repeating element
1206    /// (transforms.md, "evaluate"); a variable cannot name them all.
1207    fn check_several(
1208        &self,
1209        run: &Run,
1210        rule: &Rule,
1211        target: &Target,
1212        destination: Option<NodeId>,
1213        element: Option<&str>,
1214        count: usize,
1215    ) -> Result<()> {
1216        let fail = |message: String| Err(Error::in_rule(rule, message));
1217        let (Some(dest), Some(element)) = (destination, element) else {
1218            return fail(format!(
1219                "evaluate gave {count} values where one was expected"
1220            ));
1221        };
1222        let owner = run.tree.ty(dest).display();
1223        match self.types().lookup(run.tree.ty(dest), element) {
1224            Lookup::Found(found) if found.element.array => {}
1225            Lookup::Found(_) => {
1226                return fail(format!(
1227                    "evaluate gave {count} values, but `{owner}.{element}` does not repeat"
1228                ));
1229            }
1230            _ => {
1231                return fail(format!(
1232                    "evaluate gave {count} values, and whether `{owner}.{element}` repeats is not known"
1233                ));
1234            }
1235        }
1236        if let Some(variable) = &target.variable {
1237            return fail(format!(
1238                "evaluate gave {count} values; variable `{variable}` can only name one"
1239            ));
1240        }
1241        Ok(())
1242    }
1243
1244    #[allow(clippy::too_many_arguments)]
1245    async fn run_transform(
1246        &self,
1247        run: &mut Run,
1248        group: &GroupRef,
1249        rule: &Rule,
1250        target: &Target,
1251        vars: &Vars,
1252        destination: Option<NodeId>,
1253        element: Option<&str>,
1254        source_variable: Option<&str>,
1255        _at_root: bool,
1256    ) -> Result<Option<NodeId>> {
1257        let transform = target.transform.as_deref().unwrap_or("copy");
1258        let parameters = &target.parameter;
1259        let fail = |message: String| Error::in_rule(rule, message);
1260        let types = self.types();
1261        match transform {
1262            "create" => {
1263                let ty = match parameters.first() {
1264                    Some(parameter) => {
1265                        let name = self.parameter_text(run, group, parameter, vars).await?;
1266                        let context = destination
1267                            .and_then(|d| run.tree.ty(d).structure().map(|s| s.url.clone()))
1268                            .unwrap_or_else(|| CORE.to_owned());
1269                        let ty = types.structure(&self.type_name_url(&group.map, &name, &context));
1270                        (ty, Some(name))
1271                    }
1272                    None => self
1273                        .implied_type(run, group, destination, element, source_variable, vars)
1274                        .map_err(fail)?,
1275                };
1276                let created = run.tree.create(ty.0, ty.1);
1277                // A resource created outside any target element is one the map produces.
1278                if destination.is_none() && run.tree.ty(created).is_resource() {
1279                    run.produced.push(created);
1280                }
1281                Ok(Some(created))
1282            }
1283            "copy" => {
1284                let parameter = parameters
1285                    .first()
1286                    .ok_or_else(|| fail("copy needs a value".into()))?;
1287                let node = self.parameter_node(run, group, parameter, vars).await?;
1288                Ok(Some(run.tree.deep_copy(node)))
1289            }
1290            "truncate" => {
1291                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1292                let length = self.argument_text(run, group, parameters, 1, vars).await?;
1293                let length: usize = length
1294                    .trim()
1295                    .parse()
1296                    .map_err(|_| fail(format!("truncate needs a length, not `{length}`")))?;
1297                let truncated = text.chars().take(length).collect();
1298                Ok(Some(self.string(run, truncated, "string")))
1299            }
1300            "cast" => {
1301                let source = self
1302                    .parameter_node(
1303                        run,
1304                        group,
1305                        parameters
1306                            .first()
1307                            .ok_or_else(|| fail("cast needs a value".into()))?,
1308                        vars,
1309                    )
1310                    .await?;
1311                let type_name = if parameters.len() > 1 {
1312                    transforms::cast_type(
1313                        &self.argument_text(run, group, parameters, 1, vars).await?,
1314                    )
1315                } else {
1316                    // Without a type, the value takes the type of the element it is written to.
1317                    self.implied_type(run, group, destination, element, None, vars)
1318                        .ok()
1319                        .and_then(|(_, name)| name)
1320                        .ok_or_else(|| fail("cast needs a type: cast(value, 'type')".into()))?
1321                };
1322                if !run.tree.is_primitive(source) {
1323                    // A complex value casts only to its own type.
1324                    return match run.tree.fhir_type(source) {
1325                        Some(own) if own == type_name => Ok(Some(run.tree.deep_copy(source))),
1326                        own => Err(fail(format!(
1327                            "cannot cast a {} to {type_name}",
1328                            own.as_deref().unwrap_or("complex value")
1329                        ))),
1330                    };
1331                }
1332                let text = run.tree.text(source).unwrap_or_default();
1333                let value = transforms::cast(&text, &type_name).map_err(fail)?;
1334                let ty = types.structure(&type_url(&type_name, CORE));
1335                Ok(Some(run.tree.primitive(value, ty, Some(type_name))))
1336            }
1337            "append" => {
1338                let mut text = String::new();
1339                for index in 0..parameters.len() {
1340                    text.push_str(
1341                        &self
1342                            .argument_text(run, group, parameters, index, vars)
1343                            .await?,
1344                    );
1345                }
1346                Ok(Some(self.string(run, text, "string")))
1347            }
1348            "translate" => self.translate(run, group, rule, parameters, vars).await,
1349            "reference" | "pointer" => {
1350                let parameter = parameters
1351                    .first()
1352                    .ok_or_else(|| fail(format!("{transform} needs a resource")))?;
1353                let resource = self.parameter_node(run, group, parameter, vars).await?;
1354                let resource_type = run
1355                    .tree
1356                    .fhir_type(resource)
1357                    .filter(|_| {
1358                        run.tree.ty(resource).is_resource() || !run.tree.is_primitive(resource)
1359                    })
1360                    .ok_or_else(|| fail(format!("{transform} needs a resource")))?;
1361                let id = match run
1362                    .tree
1363                    .children(resource, "id")
1364                    .first()
1365                    .and_then(|&id| run.tree.text(id))
1366                {
1367                    Some(id) => id,
1368                    None => {
1369                        let id = self.ids.uuid();
1370                        run.tree
1371                            .set_text(resource, "id", &id, &types)
1372                            .map_err(fail)?;
1373                        id
1374                    }
1375                };
1376                Ok(Some(if transform == "reference" {
1377                    self.string(run, format!("{resource_type}/{id}"), "string")
1378                } else {
1379                    self.string(run, format!("urn:uuid:{id}"), "uri")
1380                }))
1381            }
1382            "uuid" => Ok(Some(self.string(run, self.ids.uuid(), "id"))),
1383            "toDateTime" | "toDate" | "toTime" => {
1384                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1385                let format = match parameters.get(1) {
1386                    Some(_) => Some(self.argument_text(run, group, parameters, 1, vars).await?),
1387                    None => None,
1388                };
1389                let (value, type_name) = match transform {
1390                    "toDateTime" => (datetime::to_date_time(&text, format.as_deref()), "dateTime"),
1391                    "toDate" => (datetime::to_date(&text, format.as_deref()), "date"),
1392                    _ => (datetime::to_time(&text, format.as_deref()), "time"),
1393                };
1394                Ok(Some(self.string(run, value.map_err(fail)?, type_name)))
1395            }
1396            "unixToDateTime" | "unixToDate" | "unixToTime" => {
1397                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1398                let millis: i64 = text.trim().parse().map_err(|_| {
1399                    fail(format!("`{text}` is not a Unix timestamp in milliseconds"))
1400                })?;
1401                let timezone = match parameters.get(1) {
1402                    Some(_) => Some(self.argument_text(run, group, parameters, 1, vars).await?),
1403                    None => None,
1404                };
1405                let (value, type_name) = match transform {
1406                    "unixToDateTime" => (
1407                        datetime::unix_to_date_time(millis, timezone.as_deref()),
1408                        "dateTime",
1409                    ),
1410                    "unixToDate" => (datetime::unix_to_date(millis, timezone.as_deref()), "date"),
1411                    _ => (datetime::unix_to_time(millis, timezone.as_deref()), "time"),
1412                };
1413                Ok(Some(self.string(run, value.map_err(fail)?, type_name)))
1414            }
1415            "escape" => {
1416                let text = self.argument_text(run, group, parameters, 0, vars).await?;
1417                let (from, to) = match parameters.len() {
1418                    3 => (
1419                        self.argument_text(run, group, parameters, 1, vars).await?,
1420                        self.argument_text(run, group, parameters, 2, vars).await?,
1421                    ),
1422                    _ => (
1423                        "none".to_owned(),
1424                        self.argument_text(run, group, parameters, 1, vars).await?,
1425                    ),
1426                };
1427                let escaped = datetime::escape(&text, &from, &to).map_err(fail)?;
1428                Ok(Some(self.string(run, escaped, "string")))
1429            }
1430            "cc" | "c" | "id" | "cp" | "qty" => {
1431                let mut arguments = Vec::with_capacity(parameters.len());
1432                for index in 0..parameters.len() {
1433                    arguments.push(
1434                        self.argument_text(run, group, parameters, index, vars)
1435                            .await?,
1436                    );
1437                }
1438                let (json, type_name) = match transform {
1439                    "cc" => (transforms::codeable_concept(&arguments), "CodeableConcept"),
1440                    "c" => match arguments.as_slice() {
1441                        [system, code, rest @ ..] => {
1442                            (transforms::coding(system, code, rest.first()), "Coding")
1443                        }
1444                        _ => return Err(fail("c needs a system and a code".into())),
1445                    },
1446                    "id" => (transforms::identifier(&arguments), "Identifier"),
1447                    "cp" => (transforms::contact_point(&arguments), "ContactPoint"),
1448                    _ => (transforms::quantity(&arguments).map_err(fail)?, "Quantity"),
1449                };
1450                let context = destination
1451                    .and_then(|d| run.tree.ty(d).structure().map(|s| s.url.clone()))
1452                    .unwrap_or_else(|| CORE.to_owned());
1453                let ty = types.structure(&type_url(type_name, &context));
1454                let node = run.tree.load(&json, ty, &types);
1455                Ok(Some(node))
1456            }
1457            other => Err(fail(format!("the {other} transform is not supported"))),
1458        }
1459    }
1460
1461    /// The type `create()` makes without a type argument: the element's only type, or the
1462    /// source's type.
1463    fn implied_type(
1464        &self,
1465        run: &Run,
1466        group: &GroupRef,
1467        destination: Option<NodeId>,
1468        element: Option<&str>,
1469        source_variable: Option<&str>,
1470        vars: &Vars,
1471    ) -> std::result::Result<(Type, Option<String>), String> {
1472        let types = self.types();
1473        if let (Some(dest), Some(element)) = (destination, element)
1474            && let Lookup::Found(found) = types.lookup(run.tree.ty(dest), element)
1475            && let [single] = found.element.types.as_slice()
1476            && single != "Resource"
1477            && single != "*"
1478        {
1479            return Ok((types.element_type(&found, None), Some(single.clone())));
1480        }
1481        if let Some(source) = source_variable.and_then(|s| vars.get(Mode::Input, s)) {
1482            // The type the default group for the source's type produces, as the reference engine
1483            // does: a contained R4 Medication becomes the R5 Medication its group maps to.
1484            if let Some(source_type) = run.tree.fhir_type(source)
1485                && let Some(produced) = self.default_target_type(group, &source_type)
1486            {
1487                return Ok(produced);
1488            }
1489            return Ok((run.tree.ty(source).clone(), run.tree.fhir_type(source)));
1490        }
1491        Err("cannot tell what type to create: name it, as in create('Type')".into())
1492    }
1493
1494    /// The target type of the `<<type+>>` group whose source is `source_type`, in the map or the
1495    /// maps it imports.
1496    fn default_target_type(&self, group: &GroupRef, source_type: &str) -> Option<TypedTarget> {
1497        if let Some(found) = self.default_target_in(&group.map, source_type) {
1498            return Some(found);
1499        }
1500        let key = (imports_key(&group.map), source_type.to_owned());
1501        if let Some(found) = self.cache().default_target.get(&key) {
1502            return found.clone();
1503        }
1504        let found = self.find_default_target_type(group, source_type);
1505        self.cache().default_target.insert(key, found.clone());
1506        found
1507    }
1508
1509    /// The target type of a `<<type+>>` group of `map` itself whose source is `source_type`.
1510    fn default_target_in(&self, map: &StructureMap, source_type: &str) -> Option<TypedTarget> {
1511        map.group.iter().find_map(|candidate| {
1512            if candidate.type_mode.as_deref() != Some("type-and-types") {
1513                return None;
1514            }
1515            let [source, target] = candidate.input.as_slice() else {
1516                return None;
1517            };
1518            if source.mode != "source"
1519                || target.mode != "target"
1520                || !self.stated_type_is(map, source.type_name.as_deref(), source_type)
1521            {
1522                return None;
1523            }
1524            let stated = target.type_name.as_deref()?;
1525            let ty = self
1526                .types()
1527                .structure(&self.type_name_url(map, stated, CORE));
1528            let name = ty.fhir_type().unwrap_or_else(|| stated.to_owned());
1529            Some((ty, Some(name)))
1530        })
1531    }
1532
1533    fn find_default_target_type(&self, group: &GroupRef, source_type: &str) -> Option<TypedTarget> {
1534        group.map.import.iter().find_map(|import| {
1535            self.maps
1536                .resolve(import)
1537                .iter()
1538                .filter(|imported| imported.url != group.map.url)
1539                .find_map(|imported| self.default_target_in(imported, source_type))
1540        })
1541    }
1542
1543    /// `translate(value, map, output)`: a code, Coding or CodeableConcept translated with a
1544    /// ConceptMap contained in the map (`'#id'`) or found by the ConceptMap resolver.
1545    async fn translate(
1546        &self,
1547        run: &mut Run,
1548        group: &GroupRef,
1549        rule: &Rule,
1550        parameters: &[Parameter],
1551        vars: &Vars,
1552    ) -> Result<Option<NodeId>> {
1553        let fail = |message: String| Error::in_rule(rule, message);
1554        let source = self
1555            .parameter_node(
1556                run,
1557                group,
1558                parameters
1559                    .first()
1560                    .ok_or_else(|| fail("translate needs a value".into()))?,
1561                vars,
1562            )
1563            .await?;
1564        let map_url = self.argument_text(run, group, parameters, 1, vars).await?;
1565        let output = self.argument_text(run, group, parameters, 2, vars).await?;
1566        let concept_map = match map_url.strip_prefix('#') {
1567            Some(id) => Some(
1568                group
1569                    .map
1570                    .contained
1571                    .iter()
1572                    .find(|r| r["resourceType"] == "ConceptMap" && r["id"] == id)
1573                    .cloned()
1574                    .map(Arc::new)
1575                    .ok_or_else(|| fail(format!("no contained ConceptMap `{id}`")))?,
1576            ),
1577            None => self.concept_maps.resolve(&map_url),
1578        };
1579        if concept_map.is_none() && self.translator.is_none() {
1580            return Err(fail(format!("ConceptMap `{map_url}` not found")));
1581        }
1582        let codes = self.codes_of(run, source);
1583        let mut translation = None;
1584        for (system, code) in &codes {
1585            let found = match (&concept_map, self.translator) {
1586                (Some(concept_map), _) => translate(concept_map, system.as_deref(), code),
1587                (None, Some(translator)) => {
1588                    translator
1589                        .translate(&map_url, system.as_deref(), code)
1590                        .await
1591                }
1592                (None, None) => unreachable!("checked above"),
1593            }
1594            .map_err(|e| fail(format!("{e} in `{map_url}`")))?;
1595            if let Some(found) = found {
1596                translation = Some(found);
1597                break;
1598            }
1599        }
1600        let Some(translation) = translation else {
1601            let shown: Vec<&str> = codes.iter().map(|(_, c)| c.as_str()).collect();
1602            let rule_name = rule.name.as_deref().unwrap_or("?");
1603            run.warnings.push(format!(
1604                "rule `{rule_name}`: `{}` has no translation in `{map_url}`; left out",
1605                shown.join(", ")
1606            ));
1607            return Ok(None);
1608        };
1609
1610        let types = self.types();
1611        let mut coding = serde_json::Map::new();
1612        if let Some(system) = &translation.system {
1613            coding.insert("system".into(), system.clone().into());
1614        }
1615        if let Some(code) = &translation.code {
1616            coding.insert("code".into(), code.clone().into());
1617        }
1618        if let Some(display) = &translation.display {
1619            coding.insert("display".into(), display.clone().into());
1620        }
1621        let text = |value: &Option<String>| value.clone().map_or(Value::Null, Value::String);
1622        let (json, type_name) = match output.as_str() {
1623            "code" => (text(&translation.code), "code"),
1624            "system" => (text(&translation.system), "uri"),
1625            "display" => (text(&translation.display), "string"),
1626            "Coding" => (Value::Object(coding), "Coding"),
1627            "CodeableConcept" => (
1628                json!({ "coding": [Value::Object(coding)] }),
1629                "CodeableConcept",
1630            ),
1631            other => return Err(fail(format!("unknown translate output `{other}`"))),
1632        };
1633        let ty = types.structure(&type_url(type_name, CORE));
1634        Ok(Some(if json.is_object() {
1635            run.tree.load(&json, ty, &types)
1636        } else {
1637            run.tree.primitive(json, ty, Some(type_name.to_owned()))
1638        }))
1639    }
1640
1641    /// The (system, code) pairs a value holds: a code, a Coding, or a CodeableConcept's codings.
1642    fn codes_of(&self, run: &Run, node: NodeId) -> Vec<(Option<String>, String)> {
1643        let text = |id: NodeId, name: &str| {
1644            run.tree
1645                .children(id, name)
1646                .first()
1647                .and_then(|&child| run.tree.text(child))
1648        };
1649        if run.tree.is_primitive(node) {
1650            return run
1651                .tree
1652                .text(node)
1653                .map(|code| (None, code))
1654                .into_iter()
1655                .collect();
1656        }
1657        let codings = run.tree.children(node, "coding");
1658        let codings = if codings.is_empty() {
1659            vec![node]
1660        } else {
1661            codings
1662        };
1663        codings
1664            .into_iter()
1665            .filter_map(|coding| Some((text(coding, "system"), text(coding, "code")?)))
1666            .collect()
1667    }
1668
1669    // ---- Parameters -------------------------------------------------------------------------
1670
1671    /// A parameter as a node: a variable (or constant), or a new primitive for a literal.
1672    async fn parameter_node(
1673        &self,
1674        run: &mut Run,
1675        group: &GroupRef,
1676        parameter: &Parameter,
1677        vars: &Vars,
1678    ) -> Result<NodeId> {
1679        let types = self.types();
1680        let (value, type_name) = match &parameter.value {
1681            ParameterValue::Id(name) => return self.variable(run, group, name, vars).await,
1682            ParameterValue::String(v) => (Value::String(v.clone()), "string"),
1683            ParameterValue::Boolean(v) => (Value::Bool(*v), "boolean"),
1684            ParameterValue::Integer(v) => (Value::from(*v), "integer"),
1685            ParameterValue::Decimal(v) => (Value::Number(v.clone()), "decimal"),
1686            ParameterValue::Date(v) => (Value::String(v.clone()), "date"),
1687            ParameterValue::Time(v) => (Value::String(v.clone()), "time"),
1688            ParameterValue::DateTime(v) => (Value::String(v.clone()), "dateTime"),
1689        };
1690        let ty = types.structure(&type_url(type_name, CORE));
1691        Ok(run.tree.primitive(value, ty, Some(type_name.to_owned())))
1692    }
1693
1694    /// A variable, or failing that a constant of the map.
1695    async fn variable(
1696        &self,
1697        run: &mut Run,
1698        group: &GroupRef,
1699        name: &str,
1700        vars: &Vars,
1701    ) -> Result<NodeId> {
1702        if let Some(node) = vars.value(name) {
1703            return Ok(node);
1704        }
1705        // A dotted id such as `src.code` is a path from a variable.
1706        if let Some((variable, path)) = name.split_once('.')
1707            && let Some(mut node) = vars.value(variable)
1708        {
1709            for segment in path.split('.') {
1710                node = *run
1711                    .tree
1712                    .children(node, segment)
1713                    .first()
1714                    .ok_or_else(|| Error::new(format!("`{name}` has no value")))?;
1715            }
1716            return Ok(node);
1717        }
1718        let values = self.constant(run, group, name).await?;
1719        match values.as_slice() {
1720            [single] => Ok(*single),
1721            [] => Err(Error::new(format!("unknown variable `{name}`"))),
1722            _ => Err(Error::new(format!("constant `{name}` has several values"))),
1723        }
1724    }
1725
1726    async fn parameter_text(
1727        &self,
1728        run: &mut Run,
1729        group: &GroupRef,
1730        parameter: &Parameter,
1731        vars: &Vars,
1732    ) -> Result<String> {
1733        if let ParameterValue::String(text) = &parameter.value {
1734            return Ok(text.clone());
1735        }
1736        let node = self.parameter_node(run, group, parameter, vars).await?;
1737        Ok(run.tree.text(node).unwrap_or_default())
1738    }
1739
1740    async fn argument_text(
1741        &self,
1742        run: &mut Run,
1743        group: &GroupRef,
1744        parameters: &[Parameter],
1745        index: usize,
1746        vars: &Vars,
1747    ) -> Result<String> {
1748        let parameter = parameters
1749            .get(index)
1750            .ok_or_else(|| Error::new(format!("missing argument {}", index + 1)))?;
1751        self.parameter_text(run, group, parameter, vars).await
1752    }
1753
1754    fn string(&self, run: &mut Run, text: String, type_name: &str) -> NodeId {
1755        let ty = self.types().structure(&type_url(type_name, CORE));
1756        run.tree
1757            .primitive(Value::String(text), ty, Some(type_name.to_owned()))
1758    }
1759
1760    // ---- FHIRPath ---------------------------------------------------------------------------
1761
1762    /// The value of the map's `let name = …;`, evaluated once.
1763    fn constant<'r>(
1764        &'r self,
1765        run: &'r mut Run,
1766        group: &'r GroupRef,
1767        name: &'r str,
1768    ) -> BoxFuture<'r, Result<Vec<NodeId>>> {
1769        Box::pin(async move {
1770            let key = (group.map.url.clone().unwrap_or_default(), name.to_owned());
1771            if let Some(values) = run.constants.get(&key) {
1772                return Ok(values.clone());
1773            }
1774            let Some(constant) = group.map.constants.iter().find(|c| c.name == name) else {
1775                return Ok(Vec::new());
1776            };
1777            if !run.evaluating.insert(key.clone()) {
1778                return Err(Error::new(format!("constant `{name}` refers to itself")));
1779            }
1780            let values = self
1781                .evaluate(run, group, &constant.value, None, &Vars::default())
1782                .await;
1783            run.evaluating.remove(&key);
1784            let values = values?;
1785            run.constants.insert(key, values.clone());
1786            Ok(values)
1787        })
1788    }
1789
1790    fn evaluate<'r>(
1791        &'r self,
1792        run: &'r mut Run,
1793        group: &'r GroupRef,
1794        expression: &'r str,
1795        focus: Option<NodeId>,
1796        vars: &'r Vars,
1797    ) -> BoxFuture<'r, Result<Vec<NodeId>>> {
1798        Box::pin(async move {
1799            // Only the variables the expression names: serializing every variable in scope for
1800            // every expression would copy the whole input each time.
1801            let referenced = path_start_names(expression);
1802            let mut names: HashSet<String> = vars
1803                .names()
1804                .filter(|name| referenced.contains(*name))
1805                .map(str::to_owned)
1806                .collect();
1807            let mut variables = Vec::new();
1808            for name in &names {
1809                if let Some(node) = vars.value(name) {
1810                    variables.push((name.clone(), self.item(run, node)));
1811                }
1812            }
1813            for constant in &group.map.constants {
1814                if names.contains(&constant.name) || expression == constant.value {
1815                    continue;
1816                }
1817                if referenced.contains(&constant.name) {
1818                    let values = self.constant(run, group, &constant.name).await?;
1819                    if let [value] = values.as_slice() {
1820                        variables.push((constant.name.clone(), self.item(run, *value)));
1821                        names.insert(constant.name.clone());
1822                    }
1823                }
1824            }
1825            let bound = bind_variables(expression, &names);
1826            let focus = focus.map(|node| self.item(run, node));
1827            let results = self
1828                .expressions
1829                .evaluate(&bound, focus.as_ref(), &variables)
1830                .await
1831                .map_err(|e| Error::new(format!("`{expression}`: {e}")))?;
1832            let types = self.types();
1833            Ok(results
1834                .into_iter()
1835                .map(|item| {
1836                    let ty = item
1837                        .type_name
1838                        .as_deref()
1839                        .map(|t| types.structure(&type_url(t, CORE)))
1840                        .unwrap_or(Type::Unknown);
1841                    if item.json.is_object() {
1842                        run.tree.load(&item.json, ty, &types)
1843                    } else {
1844                        run.tree.primitive(item.json, ty, item.type_name)
1845                    }
1846                })
1847                .collect())
1848        })
1849    }
1850
1851    async fn evaluate_boolean(
1852        &self,
1853        run: &mut Run,
1854        group: &GroupRef,
1855        expression: &str,
1856        focus: Option<NodeId>,
1857        vars: &Vars,
1858    ) -> Result<bool> {
1859        let values = self.evaluate(run, group, expression, focus, vars).await?;
1860        Ok(match values.as_slice() {
1861            [] => false,
1862            [single] => run.tree.value(*single) != Some(&Value::Bool(false)),
1863            _ => true,
1864        })
1865    }
1866
1867    /// A node for FHIRPath. A backbone element is named by its path (`QuestionnaireResponse.item`),
1868    /// as FHIRPath models it, so its elements keep their FHIR types.
1869    fn item(&self, run: &Run, node: NodeId) -> Item {
1870        let type_name = match run.tree.ty(node) {
1871            backbone @ Type::Node { path, .. } if !path.is_empty() => Some(backbone.display()),
1872            _ => run.tree.fhir_type(node),
1873        };
1874        Item {
1875            json: run.tree.to_json(node),
1876            type_name,
1877        }
1878    }
1879}
1880
1881/// The element path of a source or target: the element and the segments in the element
1882/// extension.
1883fn element_path(
1884    element: Option<&str>,
1885    extensions: &[fml_model::model::Extension],
1886    url: &str,
1887) -> Vec<String> {
1888    element
1889        .map(str::to_owned)
1890        .into_iter()
1891        .chain(
1892            extensions
1893                .iter()
1894                .filter(|e| e.url == url)
1895                .filter_map(|e| e.value_string.clone()),
1896        )
1897        .collect()
1898}
1899
1900/// Target list modes (the `StructureMapTargetListMode` codes; the IG leaves them to be
1901/// documented): a `first` rule's values go before the others in a repeating element and a `last`
1902/// rule's after them, one rule each per element; `single` allows one value per element.
1903fn order_target(
1904    run: &mut Run,
1905    rule: &Rule,
1906    target: &Target,
1907    dest: NodeId,
1908    element: &str,
1909    value: NodeId,
1910) -> Result<()> {
1911    let identity = std::ptr::from_ref(target) as usize;
1912    let mode = target
1913        .list_mode
1914        .iter()
1915        .find(|mode| matches!(mode.as_str(), "first" | "last" | "single"))
1916        .map(String::as_str);
1917    if mode == Some("single") && !run.single.insert((dest, element.to_owned(), identity)) {
1918        return Err(Error::in_rule(
1919            rule,
1920            format!("target `{element}` is `single`, but the rule makes more than one"),
1921        ));
1922    }
1923    let key = (dest, element.to_owned());
1924    let order = match (mode, run.order.contains_key(&key)) {
1925        (Some("first" | "last"), _) | (_, true) => run.order.entry(key).or_default(),
1926        _ => return Ok(()),
1927    };
1928    match mode {
1929        Some("first") => {
1930            if order.first.is_some_and(|first| first != identity) {
1931                return Err(Error::in_rule(
1932                    rule,
1933                    format!("two rules put their values `first` in `{element}`"),
1934                ));
1935            }
1936            order.first = Some(identity);
1937            let at = order.first_count;
1938            order.first_count += 1;
1939            run.tree.move_value(dest, element, value, Some(at));
1940        }
1941        Some("last") => {
1942            if order.last.is_some_and(|last| last != identity) {
1943                return Err(Error::in_rule(
1944                    rule,
1945                    format!("two rules put their values `last` in `{element}`"),
1946                ));
1947            }
1948            order.last = Some(identity);
1949            order.last_values.push(value);
1950        }
1951        _ => {}
1952    }
1953    let last_values = order.last_values.clone();
1954    for last in last_values {
1955        run.tree.move_value(dest, element, last, None);
1956    }
1957    Ok(())
1958}
1959
1960/// A source's `min..max`: the element must have that many values (fml.md, "Source Content").
1961fn check_cardinality(rule: &Rule, source: &Source, count: usize) -> Result<()> {
1962    let below = source
1963        .min
1964        .is_some_and(|min| i64::try_from(count).is_ok_and(|count| count < min));
1965    let above = source
1966        .max
1967        .as_deref()
1968        .and_then(|max| max.parse::<usize>().ok())
1969        .is_some_and(|max| count > max);
1970    if below || above {
1971        let path = std::iter::once(source.context.as_str())
1972            .chain(source.element.as_deref())
1973            .collect::<Vec<_>>()
1974            .join(".");
1975        return Err(Error::in_rule(
1976            rule,
1977            format!(
1978                "`{path}` has {count} values, outside {}..{}",
1979                source.min.unwrap_or(0),
1980                source.max.as_deref().unwrap_or("*")
1981            ),
1982        ));
1983    }
1984    Ok(())
1985}
1986
1987/// The key of a map's imports in the group cache: its URL and what it imports.
1988fn imports_key(map: &StructureMap) -> String {
1989    let mut key = map.url.clone().unwrap_or_default();
1990    for import in &map.import {
1991        key.push('\n');
1992        key.push_str(import);
1993    }
1994    key
1995}
1996
1997/// A dependent group argument's variable name.
1998fn parameter_name(parameter: &Parameter) -> String {
1999    match &parameter.value {
2000        ParameterValue::Id(name) | ParameterValue::String(name) => name.clone(),
2001        other => format!("{other:?}"),
2002    }
2003}
2004
2005/// The variables of a rule that copies one value to one target with nothing else to do.
2006fn simple_copy(rule: &Rule) -> Option<(&str, &str)> {
2007    let ([source], [target]) = (rule.source.as_slice(), rule.target.as_slice()) else {
2008        return None;
2009    };
2010    (target.transform.as_deref() == Some("create") && target.parameter.is_empty()).then_some(())?;
2011    Some((source.variable.as_deref()?, target.variable.as_deref()?))
2012}