fhirpath 0.2.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/docs/api.md ADDED
@@ -0,0 +1,170 @@
1
+ # FHIRPath Ruby API reference
2
+
3
+ Status: pre-release API contract (`0.2.0.pre1`)
4
+
5
+ This document describes the Ruby-native public surface. It is a contract for this project, not a claim of source compatibility with `fhirpath-py`, `fhirpath.js`, HAPI, or Firely.
6
+
7
+ ## Entry points
8
+
9
+ ```ruby
10
+ require "fhirpath"
11
+ ```
12
+
13
+ ### `FHIRPath.parse`
14
+
15
+ ```ruby
16
+ parsed = FHIRPath.parse(expression, capability: FHIRPath::Capability.current)
17
+ ```
18
+
19
+ Returns an immutable `FHIRPath::ParsedExpression` with:
20
+
21
+ - `source`: the original expression;
22
+ - `ast`: immutable `FHIRPath::AST` nodes; and
23
+ - `source_map`: a node-to-`FHIRPath::SourceSpan` map.
24
+
25
+ A blank, malformed, unsupported-token, or trailing-input expression raises `FHIRPath::ParseError`. The error exposes `code`, `span`, `expression`, and `to_h`.
26
+
27
+ Passing a `String` to `parse` never freezes that string: `parse` retains an internal frozen snapshot of the expression. You may mutate your own source string after calling `parse` without affecting the returned `ParsedExpression`.
28
+
29
+ ### `FHIRPath.compile`
30
+
31
+ ```ruby
32
+ program = FHIRPath.compile(
33
+ expression,
34
+ model: nil,
35
+ capability: FHIRPath::Capability.current,
36
+ functions: FHIRPath::FunctionRegistry.standard
37
+ )
38
+ ```
39
+
40
+ Returns a frozen `FHIRPath::CompiledExpression`. Parsing happens once; each `evaluate` or `call` creates fresh per-evaluation context. A compiled expression must not retain a resource, variables, focus, trace state, or mutable evaluation cache between calls.
41
+
42
+ `compile` likewise never freezes the caller's source `String`: it snapshots the expression internally. Mutating the string you passed after calling `compile` does not change the compiled program, and passing an already-frozen string works normally.
43
+
44
+ `model` defaults to `FHIRPath::PlainModel`. Pass `model: :r4` (or `model: 'R4'`)
45
+ to select the dependency-free `FHIRPath::FHIR::R4::ModelProvider`. Passing a
46
+ provider object remains supported for custom model adapters. `functions` is an
47
+ immutable function registry snapshot.
48
+
49
+ ### `FHIRPath.evaluate`
50
+
51
+ ```ruby
52
+ result = FHIRPath.evaluate(
53
+ resource,
54
+ expression,
55
+ variables: {},
56
+ model: nil,
57
+ capability: FHIRPath::Capability.current,
58
+ functions: FHIRPath::FunctionRegistry.standard,
59
+ options: {},
60
+ host: nil
61
+ )
62
+ ```
63
+
64
+ Returns a `FHIRPath::Collection`. Empty results are collections with `empty? == true`, not `nil`. `to_a` returns a copy of the ordered values.
65
+
66
+ `variables:` supplies external constants using either String or Symbol keys:
67
+
68
+ ```ruby
69
+ FHIRPath.evaluate({}, "%enabled", variables: { enabled: false }).to_a
70
+ # => [false]
71
+ ```
72
+
73
+ `host:` is reserved for explicit host services. Pure evaluation does not perform network I/O.
74
+
75
+ ### Host constants
76
+
77
+ External constants can be supplied by an immutable `HostServices` configuration:
78
+
79
+ ```ruby
80
+ provider = Class.new(FHIRPath::ConstantProvider) do
81
+ def fetch(name, mode:, context:)
82
+ { 'tenant' => 'example' }.fetch(name)
83
+ end
84
+ end.new
85
+
86
+ host = FHIRPath::HostServices.new(constant_provider: provider)
87
+ FHIRPath.evaluate({}, '%tenant', host: host).to_a
88
+ # => ["example"]
89
+ ```
90
+
91
+ `ConstantProvider#fetch(name, mode:, context:)` is the only provider boundary used by this slice. The engine does not discover constants or perform filesystem/network I/O. `variables:` takes precedence over the provider. Without a provider, `%name` raises `UnknownConstantError` with code `:unknown_constant`. Constant-provider failures raise a generic `HostError`; exceptions raised by the constant provider are not retained as public causes, and their detail is omitted from the public error message, `full_message`, and `to_h` serialization. Reference resolution, terminology, tracing, and cache ownership remain deferred host-service slices.
92
+
93
+ ### `FHIRPath.evaluate_first`
94
+
95
+ Has the same options as `evaluate` and returns the first item or `nil` for an empty result. It is a convenience at the API boundary; it does not permit multi-item singleton coercion inside the evaluator.
96
+
97
+ ## Errors
98
+
99
+ All public engine errors derive from `FHIRPath::Error` and carry a stable symbolic `code`, optional `span`, optional original `expression`, and `to_h` serialization.
100
+
101
+ | Error | Meaning |
102
+ |---|---|
103
+ | `ParseError` | Invalid token, malformed syntax, unsupported escape, trailing input, or expression nesting exceeding the parser depth budget (code `nesting_depth_exceeded`) |
104
+ | `EvaluationError` | Valid syntax cannot be evaluated for the current input |
105
+ | `SingletonError` | A singleton value was required but the collection had multiple items |
106
+ | `FHIRPath::TypeError` | A value has an incompatible FHIRPath type |
107
+ | `UnknownFunctionError` | No standard or registered function exists |
108
+ | `UnknownConstantError` | An external constant was not supplied |
109
+ | `ModelError` | Model navigation or type resolution failed |
110
+ | `HostError` | An injected host service failed |
111
+ | `UnsupportedFeatureError` | The construct is known but not implemented or enabled |
112
+
113
+ The project defines `FHIRPath::TypeError` inside its namespace; callers should qualify it to avoid confusion with Ruby's built-in `TypeError`.
114
+
115
+ ## Collections and values
116
+
117
+ `FHIRPath::Collection` is ordered, enumerable, immutable, and flattening at construction. `singleton!` returns the only item or raises `SingletonError`; `first_item` returns the first item or `nil`. The evaluator preserves FHIRPath empty/singleton/multi-item semantics instead of using Ruby truthiness.
118
+
119
+ For the implemented string operators, `+` concatenates two singleton strings but propagates an empty operand, while `&` treats each empty operand as the empty string. Thus `'a' + {}` is empty, whereas `'a' & {}` returns `['a']`. String escapes follow the FHIRPath `\\uXXXX` form; valid UTF-16 surrogate pairs are combined, and unknown forms such as `\\U0001F600` are rejected with `ParseError`.
120
+
121
+ Most current public results are ordinary Ruby values. `FHIRPath::Value::*` and `FHIRPath::TypeInfo` provide extension boundaries for semantic values and model metadata. The dependency-free R4 adapter supports JSON choice navigation for `Observation.value[x]`; a resolved choice value carries its FHIR logical type (for example `Quantity` for `valueQuantity`), so `is`/`as` type operators can test it. `Collection` may carry positional model-type metadata alongside items without changing item values; the metadata is attached when a collection is produced directly by model navigation, and operators that rebuild collections (such as `union`) do not yet propagate it. Date/time, quantity, broader type-aware model behavior (resource hierarchies, `ofType()`), and other FHIR releases remain deferred.
122
+
123
+ ## Custom functions
124
+
125
+ Create a new immutable registry rather than mutating the standard registry:
126
+
127
+ ```ruby
128
+ registry = FHIRPath::FunctionRegistry.standard.register(
129
+ FHIRPath::FunctionSpec.new(
130
+ name: "triple",
131
+ arity: 0,
132
+ receiver: :collection,
133
+ implementation: ->(_receiver, _arguments, _context) { [3] }
134
+ )
135
+ )
136
+
137
+ FHIRPath.evaluate({}, "triple()", functions: registry).to_a
138
+ # => [3]
139
+ ```
140
+
141
+ `arity` may be an integer or range. `parameters` describes argument kinds; delayed arguments are passed as AST expressions. Standard functions cannot be replaced. Function callbacks should raise `FHIRPath::Error` subclasses when reporting engine-level failures and must not mutate the resource or shared registry.
142
+
143
+ ## Capability
144
+
145
+ `FHIRPath::Capability.current` declares the default `fhirpath` release
146
+ (`2.0.0`) and the bundled FHIR model release (`R4`). It lists `trial_use`,
147
+ `model_releases`, and `host_features`. Capabilities are immutable.
148
+
149
+ The normative 2.0.0 core keeps `fhirpath: '2.0.0'` and an unchanged
150
+ `capability_set`. As a documented, default-on exception, the standard registry
151
+ ships the FHIRPath 3.0.0 STU3 aggregate functions `sum()`, `avg()`, `max()`,
152
+ and `min()` (normative 2.0.0 contains only `count()`). `Capability.current`
153
+ surfaces this subset in `trial_use` under the marker
154
+ `stu3-aggregate-functions`, so the capability report (`Capability#to_h`) names
155
+ the STU3 behavior it ships instead of silently folding it into the normative
156
+ claims. Construct `FHIRPath::Capability.new(trial_use: [])` to obtain a
157
+ declaration without the subset; `supports?('stu3-aggregate-functions')`
158
+ reports whether a capability declares it.
159
+
160
+ `trial_use` is a surface declaration, not a registry gate: it does not enable
161
+ or disable functions in `FHIRPath::FunctionRegistry.standard`, and passing a
162
+ strict-2.0 capability to `parse`/`compile`/`evaluate` does not remove the
163
+ aggregate functions from the standard registry. A caller that requires a hard
164
+ 2.0.0-only function set must supply its own registry that omits them.
165
+ A capability declaration is not proof that all standard functions or FHIR
166
+ model behavior is implemented; consult [the feature matrix](feature-matrix.md).
167
+
168
+ ## Stability policy
169
+
170
+ The pre-1.0 API may change between releases. Changes to result shape, keyword arguments, error fields, or supported expressions must be recorded in `CHANGELOG.md`, covered by API tests, and called out in release notes. No compatibility promise is made for internal AST node classes beyond their current immutable/source-span design.