1pub mod convert;
14pub mod equality;
15pub mod error;
16mod functions;
17mod navigate;
18mod operators;
19mod order;
20
21use std::collections::BTreeMap;
22use std::fmt;
23use std::sync::Arc;
24
25use fhir_types::codec::Value;
26
27use crate::ast::{Expr, Literal};
28use crate::model::{Model, named_type, path_type};
29use crate::quantity::Quantity;
30use crate::seam::{Definitions, Profiles, Resolver, Terminology};
31use crate::value::node::{Doc, Node};
32use crate::value::{Collection, Item};
33use convert::UCUM;
34use error::EvalError;
35
36pub const STU3_FUNCTIONS: [&str; 13] = [
45 "comparable",
46 "decode",
47 "encode",
48 "escape",
49 "highBoundary",
50 "join",
51 "lowBoundary",
52 "matchesFull",
53 "precision",
54 "sort",
55 "split",
56 "trim",
57 "unescape",
58];
59
60pub struct Environment<'a> {
63 model: &'a dyn Model,
64 variables: BTreeMap<String, Collection<'a>>,
65 resolver: Option<&'a dyn Resolver>,
66 terminology: Option<&'a dyn Terminology>,
67 profiles: Option<&'a dyn Profiles>,
68 definitions: Option<&'a dyn Definitions>,
69 now: Option<jiff::Timestamp>,
70 strict_ordering: bool,
71 stu3_functions: bool,
72}
73
74impl fmt::Debug for Environment<'_> {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 f.debug_struct("Environment")
77 .field("model", &self.model)
78 .field("variables", &self.variables.keys().collect::<Vec<_>>())
79 .field("resolver", &self.resolver.is_some())
80 .field("terminology", &self.terminology.is_some())
81 .field("profiles", &self.profiles.is_some())
82 .field("definitions", &self.definitions.is_some())
83 .field("now", &self.now)
84 .field("strict_ordering", &self.strict_ordering)
85 .field("stu3_functions", &self.stu3_functions)
86 .finish()
87 }
88}
89
90impl<'a> Environment<'a> {
91 #[must_use]
93 pub fn new(model: &'a dyn Model) -> Self {
94 Self {
95 model,
96 variables: BTreeMap::new(),
97 resolver: None,
98 terminology: None,
99 profiles: None,
100 definitions: None,
101 now: None,
102 strict_ordering: false,
103 stu3_functions: false,
104 }
105 }
106
107 #[must_use]
110 pub fn with_variable(mut self, name: impl Into<String>, value: Collection<'a>) -> Self {
111 self.variables.insert(name.into(), value);
112 self
113 }
114
115 #[must_use]
117 pub fn with_resolver(mut self, resolver: &'a dyn Resolver) -> Self {
118 self.resolver = Some(resolver);
119 self
120 }
121
122 #[must_use]
124 pub fn with_terminology(mut self, terminology: &'a dyn Terminology) -> Self {
125 self.terminology = Some(terminology);
126 self
127 }
128
129 #[must_use]
131 pub fn with_profiles(mut self, profiles: &'a dyn Profiles) -> Self {
132 self.profiles = Some(profiles);
133 self
134 }
135
136 #[must_use]
138 pub fn with_definitions(mut self, definitions: &'a dyn Definitions) -> Self {
139 self.definitions = Some(definitions);
140 self
141 }
142
143 #[must_use]
146 pub const fn with_now(mut self, now: jiff::Timestamp) -> Self {
147 self.now = Some(now);
148 self
149 }
150
151 #[must_use]
161 pub const fn with_strict_ordering(mut self) -> Self {
162 self.strict_ordering = true;
163 self
164 }
165
166 #[must_use]
174 pub const fn with_fhirpath_stu3_functions(mut self) -> Self {
175 self.stu3_functions = true;
176 self
177 }
178
179 #[must_use]
181 pub fn model(&self) -> &'a dyn Model {
182 self.model
183 }
184}
185
186#[derive(Clone)]
188enum Start<'a> {
189 Resource(&'a Value),
191 Element {
194 value: Option<&'a Value>,
195 element: Option<&'a Value>,
196 type_name: String,
197 definition: Option<String>,
198 },
199 Items(Collection<'a>),
201}
202
203#[derive(Clone)]
208pub struct Focus<'a> {
209 start: Start<'a>,
210 resource: Option<&'a Value>,
211 root: Option<&'a Value>,
212}
213
214impl fmt::Debug for Focus<'_> {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 let start = match &self.start {
217 Start::Resource(_) => String::from("resource"),
218 Start::Element { type_name, .. } => format!("element of {type_name}"),
219 Start::Items(items) => format!("{} items", items.len()),
220 };
221 f.debug_struct("Focus")
222 .field("start", &start)
223 .field("resource", &self.resource.is_some())
224 .field("root", &self.root.is_some())
225 .finish()
226 }
227}
228
229impl<'a> Focus<'a> {
230 #[must_use]
232 pub const fn resource(resource: &'a Value) -> Self {
233 Self {
234 start: Start::Resource(resource),
235 resource: Some(resource),
236 root: Some(resource),
237 }
238 }
239
240 #[must_use]
243 pub const fn contained(resource: &'a Value, container: &'a Value) -> Self {
244 Self {
245 start: Start::Resource(resource),
246 resource: Some(resource),
247 root: Some(container),
248 }
249 }
250
251 #[must_use]
257 pub fn element(value: &'a Value, type_name: impl Into<String>, resource: &'a Value) -> Self {
258 Self {
259 start: Start::Element {
260 value: Some(value),
261 element: None,
262 type_name: type_name.into(),
263 definition: None,
264 },
265 resource: Some(resource),
266 root: Some(resource),
267 }
268 }
269
270 #[must_use]
273 pub fn primitive(
274 value: Option<&'a Value>,
275 element: Option<&'a Value>,
276 type_name: impl Into<String>,
277 resource: &'a Value,
278 ) -> Self {
279 Self {
280 start: Start::Element {
281 value,
282 element,
283 type_name: type_name.into(),
284 definition: None,
285 },
286 resource: Some(resource),
287 root: Some(resource),
288 }
289 }
290
291 #[must_use]
299 pub fn defined_by(mut self, path: impl Into<String>) -> Self {
300 if let Start::Element { definition, .. } = &mut self.start {
301 *definition = Some(path.into());
302 }
303 self
304 }
305
306 #[must_use]
308 pub const fn items(items: Collection<'a>, resource: Option<&'a Value>) -> Self {
309 Self {
310 start: Start::Items(items),
311 resource,
312 root: resource,
313 }
314 }
315
316 #[must_use]
321 pub fn within(mut self, resource: Option<&'a Value>, root: Option<&'a Value>) -> Self {
322 self.resource = resource;
323 self.root = root;
324 self
325 }
326
327 #[must_use]
329 pub const fn none() -> Self {
330 Self {
331 start: Start::Items(Vec::new()),
332 resource: None,
333 root: None,
334 }
335 }
336}
337
338pub(crate) struct Engine<'e, 'a> {
340 env: &'e Environment<'a>,
341 context: Collection<'a>,
342 resource: Collection<'a>,
343 root: Collection<'a>,
344 now: jiff::Timestamp,
345}
346
347#[derive(Clone)]
350pub(crate) struct Frame<'a> {
351 focus: Collection<'a>,
352 index: Option<usize>,
353 total: Option<Collection<'a>>,
354}
355
356impl<'a> Frame<'a> {
357 pub(crate) const fn top(focus: Collection<'a>) -> Self {
359 Self {
360 focus,
361 index: None,
362 total: None,
363 }
364 }
365}
366
367pub(crate) fn evaluate<'a>(
373 expr: &Expr,
374 env: &Environment<'a>,
375 focus: Focus<'a>,
376) -> Result<Collection<'a>, EvalError> {
377 let model = env.model;
378 let as_resource = |value: Option<&'a Value>| -> Collection<'a> {
379 value
380 .map(|value| Item::Node(Node::resource(model, Doc::Borrowed(value))))
381 .into_iter()
382 .collect()
383 };
384 let context = match focus.start {
385 Start::Resource(value) => as_resource(Some(value)),
386 Start::Element {
387 value,
388 element,
389 type_name,
390 definition,
391 } => {
392 let ty = path_type(model, &type_name)
393 .or_else(|| named_type(model, &type_name))
394 .ok_or(EvalError::UnknownType { name: type_name })?;
395 let definition = match definition {
396 Some(path) => match model.schemas().element(&path) {
397 Some((_, field)) => Some(field.path),
398 None => return Err(EvalError::UnknownType { name: path }),
399 },
400 None => None,
401 };
402 let node = match (value, element) {
403 (Some(value), element) => Some(Node::typed(
404 ty,
405 Doc::Borrowed(value),
406 element.map(Doc::Borrowed),
407 )),
408 (None, Some(element)) => Some(Node::typed(
409 ty,
410 Doc::Owned(Arc::new(Value::Null)),
411 Some(Doc::Borrowed(element)),
412 )),
413 (None, None) => None,
414 };
415 node.map(|node| match definition {
416 Some(path) => node.with_definition(path),
417 None => node,
418 })
419 .map(Item::Node)
420 .into_iter()
421 .collect()
422 }
423 Start::Items(items) => items,
424 };
425 let engine = Engine {
426 env,
427 resource: as_resource(focus.resource),
428 root: as_resource(focus.root),
429 now: env.now.unwrap_or_else(jiff::Timestamp::now),
430 context: context.clone(),
431 };
432 engine.eval(expr, &Frame::top(context))
433}
434
435impl<'e, 'a> Engine<'e, 'a> {
436 pub(crate) fn model(&self) -> &'a dyn Model {
438 self.env.model
439 }
440
441 pub(crate) fn eval(&self, expr: &Expr, frame: &Frame<'a>) -> Result<Collection<'a>, EvalError> {
443 match expr {
444 Expr::Literal(literal) => Ok(vec![literal_item(literal)]),
445 Expr::Empty => Ok(Vec::new()),
446 Expr::External(name) => self.external(name),
447 Expr::This => Ok(frame.focus.clone()),
448 Expr::Index => Ok(frame
449 .index
450 .and_then(|index| i64::try_from(index).ok())
451 .map(Item::Integer)
452 .into_iter()
453 .collect()),
454 Expr::Total => Ok(frame.total.clone().unwrap_or_default()),
455 Expr::Member { input, name } => {
456 let items = self.input(input.as_deref(), frame)?;
457 self.navigate(&items, name, input.is_none())
458 }
459 Expr::Call { input, name, args } => {
460 if order::ORDERED_FUNCTIONS.contains(&name.as_str()) {
461 self.require_order(name, input.as_deref())?;
462 }
463 let items = self.input(input.as_deref(), frame)?;
464 self.call(name, items, args, frame)
465 }
466 Expr::Invocation { input, variable } => {
467 let items = self.eval(input, frame)?;
468 self.eval(variable, &Frame::top(items))
469 }
470 Expr::Indexer { input, index } => {
471 self.require_order("[]", Some(input))?;
472 let items = self.eval(input, frame)?;
473 let index =
474 convert::integer(self.model(), &self.eval(index, frame)?, "the indexer")?;
475 Ok(index
476 .and_then(|index| usize::try_from(index).ok())
477 .and_then(|index| items.into_iter().nth(index))
478 .into_iter()
479 .collect())
480 }
481 Expr::Negate(operand) => operators::negate(self.model(), &self.eval(operand, frame)?),
482 Expr::Plus(operand) => operators::plus(self.model(), &self.eval(operand, frame)?),
483 Expr::Binary { op, left, right } => self.binary(*op, left, right, frame),
484 Expr::Is {
485 operand,
486 type_specifier,
487 } => {
488 let items = self.eval(operand, frame)?;
489 let ty = self.resolve_type(type_specifier)?;
490 match convert::single(&items, "is")? {
491 Some(item) => Ok(vec![Item::Boolean(self.is_instance(item, &ty))]),
492 None => Ok(Vec::new()),
493 }
494 }
495 Expr::As {
496 operand,
497 type_specifier,
498 } => {
499 let items = self.eval(operand, frame)?;
500 let ty = self.resolve_type(type_specifier)?;
501 match convert::single(&items, "as")? {
502 Some(item) if self.is_type(item, &ty) => Ok(vec![item.clone()]),
503 _ => Ok(Vec::new()),
504 }
505 }
506 Expr::SortKey { key, .. } => self.eval(key, frame),
509 }
510 }
511
512 fn require_order(&self, operation: &str, input: Option<&Expr>) -> Result<(), EvalError> {
515 if self.env.strict_ordering && input.is_some_and(order::unordered) {
516 return Err(EvalError::Unordered {
517 operation: String::from(operation),
518 });
519 }
520 Ok(())
521 }
522
523 fn input(&self, input: Option<&Expr>, frame: &Frame<'a>) -> Result<Collection<'a>, EvalError> {
526 match input {
527 Some(input) => self.eval(input, frame),
528 None => Ok(frame.focus.clone()),
529 }
530 }
531
532 fn external(&self, name: &str) -> Result<Collection<'a>, EvalError> {
534 if let Some(value) = self.env.variables.get(name) {
535 return Ok(value.clone());
536 }
537 let text = |value: &str| Ok(vec![Item::String(String::from(value))]);
538 match name {
539 "ucum" => text(UCUM),
540 "sct" => text("http://snomed.info/sct"),
541 "loinc" => text("http://loinc.org"),
542 "context" => Ok(self.context.clone()),
543 "resource" => Ok(self.resource.clone()),
544 "rootResource" => Ok(self.root.clone()),
545 _ => {
546 if let Some(id) = name.strip_prefix("vs-") {
547 text(&format!("http://hl7.org/fhir/ValueSet/{id}"))
548 } else if let Some(id) = name.strip_prefix("ext-") {
549 text(&format!("http://hl7.org/fhir/StructureDefinition/{id}"))
550 } else {
551 Err(EvalError::UnknownVariable {
552 name: String::from(name),
553 })
554 }
555 }
556 }
557 }
558
559 pub(crate) fn resource(&self) -> &Collection<'a> {
561 &self.resource
562 }
563
564 pub(crate) fn root(&self) -> &Collection<'a> {
566 &self.root
567 }
568
569 pub(crate) const fn now(&self) -> jiff::Timestamp {
571 self.now
572 }
573
574 pub(crate) const fn env(&self) -> &'e Environment<'a> {
576 self.env
577 }
578}
579
580fn literal_item<'a>(literal: &Literal) -> Item<'a> {
582 match literal {
583 Literal::Boolean(value) => Item::Boolean(*value),
584 Literal::String(value) => Item::String(value.clone()),
585 Literal::Integer(value) => Item::Integer(*value),
586 Literal::Decimal(value) => Item::Decimal(*value),
587 Literal::Date(value) => Item::Date(*value),
588 Literal::DateTime(value) => Item::DateTime(*value),
589 Literal::Time(value) => Item::Time(*value),
590 Literal::Quantity(value, unit) => Item::Quantity(Quantity::new(*value, unit.clone())),
591 }
592}