ferrobridge_fhirpath/expression.rs
1// SPDX-FileCopyrightText: Cadasto B.V.
2// SPDX-License-Identifier: BUSL-1.1
3
4//! A parsed expression: parsed once, then checked and evaluated many times.
5
6use std::fmt;
7use std::str::FromStr;
8
9use crate::ast::Expr;
10use crate::check::{SemanticError, check, check_any};
11use crate::eval::error::EvalError;
12use crate::eval::{Environment, Focus, evaluate};
13use crate::model::{FhirType, Model};
14use crate::parse::{ParseError, parse};
15use crate::value::Collection;
16
17/// A FHIRPath expression, parsed.
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Expression {
20 source: String,
21 tree: Expr,
22}
23
24impl Expression {
25 /// Parses `text`.
26 ///
27 /// # Errors
28 ///
29 /// Returns [`ParseError`] when `text` is no N1 expression.
30 pub fn parse(text: &str) -> Result<Self, ParseError> {
31 Ok(Self {
32 source: String::from(text),
33 tree: parse(text)?,
34 })
35 }
36
37 /// The text the expression was parsed from.
38 #[must_use]
39 pub fn source(&self) -> &str {
40 &self.source
41 }
42
43 /// The syntax tree.
44 #[must_use]
45 pub const fn tree(&self) -> &Expr {
46 &self.tree
47 }
48
49 /// Checks the expression against the type its context will have: every
50 /// member a path names must be one the types along the path define, and
51 /// a path that starts with a type name must start with the context's (N1
52 /// §3, §11).
53 ///
54 /// # Errors
55 ///
56 /// Returns [`SemanticError`] for the first member or type name the model
57 /// refuses.
58 pub fn check(&self, model: &dyn Model, context: FhirType) -> Result<(), SemanticError> {
59 check(&self.tree, model, context)
60 }
61
62 /// Checks the expression against a context that may hold any of the
63 /// types `contexts`, as an occurrence of a choice element does (R4
64 /// §2.9.1.1, [`crate::model::path_types`]): a member is accepted when one
65 /// of them defines it, and refused when none does.
66 ///
67 /// # Errors
68 ///
69 /// Returns [`SemanticError`] for the first member or type name the model
70 /// refuses.
71 pub fn check_any(&self, model: &dyn Model, contexts: &[FhirType]) -> Result<(), SemanticError> {
72 check_any(&self.tree, model, contexts)
73 }
74
75 /// Evaluates the expression in `env` on `focus`.
76 ///
77 /// # Errors
78 ///
79 /// Returns [`EvalError`] when the evaluation signals an error; an empty
80 /// result is `Ok` with an empty collection.
81 pub fn evaluate<'a>(
82 &self,
83 env: &Environment<'a>,
84 focus: Focus<'a>,
85 ) -> Result<Collection<'a>, EvalError> {
86 evaluate(&self.tree, env, focus)
87 }
88}
89
90impl FromStr for Expression {
91 type Err = ParseError;
92
93 fn from_str(text: &str) -> Result<Self, Self::Err> {
94 Self::parse(text)
95 }
96}
97
98impl fmt::Display for Expression {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.write_str(&self.source)
101 }
102}