1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// SPDX-FileCopyrightText: Cadasto B.V.
// SPDX-License-Identifier: BUSL-1.1
//! Errors for the Simplified Formats layer.
//!
//! Every MUST-level rejection in the Simplified Formats specification
//! (`ITS-REST/docs/simplified_formats/`) has its own variant so callers can
//! branch on the outcome without string matching; the wire layer maps these
//! to `422`/`400` per the ITS-REST operation contracts.
/// An error in Simplified-Format (FLAT / STRUCTURED / Web Template) handling.
#[derive(Debug, thiserror::Error)]
pub enum FlatError {
/// The operational template lacked something the Web Template builder
/// requires.
#[error("invalid operational template: {0}")]
InvalidTemplate(String),
/// An OPT 1.4 XML document failed to parse.
#[error("failed to parse OPT 1.4 XML")]
OptParse(#[from] openehr_its::xml::runtime::XmlError),
/// A value could not be serialized to JSON.
#[error("failed to serialize to JSON")]
Serialize(#[from] serde_json::Error),
/// A FLAT key is not syntactically valid (ITS-REST simplified_formats
/// master04 §Flat format syntax rules).
#[error("malformed simplified path {path:?}: {reason}")]
MalformedPath {
/// The offending key as received.
path: String,
/// What rule the key breaks.
reason: String,
},
/// A path does not resolve to a node of the target Web Template
/// (master04 §Validation: "Field identifiers match WT metadata
/// structure").
#[error("unknown simplified path: {0}")]
UnknownPath(String),
/// An attribute suffix is not defined for the RM type at its path
/// (master05 per-type tables).
#[error("unknown attribute suffix |{suffix} for {rm_type} at {path}")]
UnknownSuffix {
/// The RM type of the node the path resolved to.
rm_type: String,
/// The offending suffix (without the pipe).
suffix: String,
/// The path the suffix was attached to.
path: String,
},
/// `|other` combined with `|code`/`|value`/`|terminology` on one leaf —
/// the combination servers MUST reject (master04 §Open Value-Sets and
/// the `|other` Suffix).
#[error("|other is mutually exclusive with |code/|value/|terminology at {0}")]
OtherSuffixConflict(String),
/// `|other` used where the value-set constraint is closed
/// (`listOpen: false`) — MUST be rejected (master04 §Open Value-Sets
/// and the `|other` Suffix).
#[error("|other not allowed at {0}: the value set is closed")]
OtherOnClosedValueSet(String),
/// A mandatory context field is absent (master04 §Context: "Mandatory:
/// language, territory").
#[error("mandatory ctx field {0} is required (master04 §Context: language and territory)")]
MissingContext(&'static str),
/// A `|code` on a closed value-set leaf names a code outside the bound
/// list and carries no explicit `|value` to supply the text
/// (master04 §Validation: "Terminology bindings are valid").
#[error("coded value is not a member of the bound value set: '{code}' at {path}")]
CodeNotInValueSet {
/// The simplified path of the coded leaf.
path: String,
/// The code the document supplied.
code: String,
},
/// A `ctx/` key outside the vocabulary of master06-context_information.
#[error("unknown context key ctx/{0}")]
UnknownContext(String),
/// A FLAT key the RM types 1..1 is absent from the submitted document:
/// either a `|suffix` the master05 mapping table marks `Required: yes` —
/// e.g. all three of §LINK's `|type`, `|meaning` and `|target`
/// (`RM/docs/UML/classes/org.openehr.rm.common.link.adoc` §Attributes) —
/// or a `ctx/` key standing in for a mandatory attribute of the RM object
/// it builds, e.g. `ctx/participation_function:<i>` for
/// `PARTICIPATION.function`
/// (`RM/docs/UML/classes/org.openehr.rm.common.participation.adoc`
/// §Attributes).
#[error("{key} is required")]
MissingRequiredSuffix {
/// The full FLAT key the client omitted, e.g. `…/_link:0|meaning` or
/// `ctx/participation_function:0`.
key: String,
},
/// A FLAT or STRUCTURED write decomposes a value of an RM type the
/// Simplified Formats define no form for: ITS-REST
/// `simplified_formats/master05-rm_mapping.adoc` has a mapping table per
/// supported type (§DV_TEXT to §DV_INTERVAL) and none for `DV_STATE`.
/// Only the master04 §Raw canonical JSON bypass (`|raw`) carries one.
#[error(
"{path} is a {rm_type}, for which the simplified formats define no form \
(simplified_formats master05-rm_mapping has no {rm_type} table); \
send it as canonical JSON with |raw (master04 §Raw canonical JSON)"
)]
NoSimplifiedForm {
/// The RM type of the node the path resolved to.
rm_type: String,
/// The simplified path of the node.
path: String,
},
/// A value has the wrong JSON type or an unparsable encoding for its
/// slot (e.g. a non-numeric `|magnitude`, a malformed compact
/// participation-identifier list).
#[error("invalid value at {path}: {reason}")]
InvalidValue {
/// The simplified path of the offending value.
path: String,
/// What is wrong with it.
reason: String,
},
/// A `|raw` value carries no `_type`, so it cannot be read as an RM
/// object (master04 §Raw canonical JSON: "The raw JSON value must include
/// the `_type` property indicating the openEHR type").
#[error("invalid |raw payload at {path}: the value must be a canonical-JSON object with _type")]
RawWithoutType {
/// The simplified path of the `|raw` key.
path: String,
},
/// A `|raw` payload is an RM object the attribute does not admit, or
/// stands beside other content at its node.
#[error("invalid |raw payload at {path}: {reason}")]
InvalidRaw {
/// The simplified path of the `|raw` key.
path: String,
/// What is wrong with the payload.
reason: String,
},
/// The input or produced structure is not the shape the converter
/// expects (e.g. the RM value at a template path is not the declared RM
/// type, or a STRUCTURED document is not a JSON object).
#[error("simplified-format conversion error: {0}")]
Conversion(String),
/// An example COMPOSITION was asked of a template whose root is not a
/// COMPOSITION (an ENTRY or a SECTION, for example), which no COMPOSITION
/// conforms to.
#[error(
"the template root {root} is not a COMPOSITION, so no example COMPOSITION exists for it"
)]
NoExampleForRoot {
/// The template root's RM type.
root: String,
},
/// A party build was asked of a template whose root is not a party class
/// (`PERSON`, `ORGANISATION`, `GROUP`, `AGENT` or `ROLE`).
#[error("the template root {root} is not a PARTY class, so no party builds from it")]
NotAPartyTemplate {
/// The template root's RM type.
root: String,
},
/// A party (or other non-COMPOSITION root) document carries a `ctx/` key:
/// the `ctx/` vocabulary is the COMPOSITION context (ITS-REST
/// `simplified_formats/master06`), and such a root has none.
#[error(
"only a COMPOSITION document takes ctx/ keys: ctx/ is the COMPOSITION context vocabulary"
)]
ContextOnParty,
/// A built party does not decode as its root class; the strict canonical
/// reader's refusal is the source.
#[error("the built party does not decode as {class}")]
PartyDecode {
/// The party class the build targeted.
class: String,
/// The canonical-JSON reader's refusal.
#[source]
source: openehr_its::json::JsonParseError,
},
}