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
// 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 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` payload is not a usable canonical-JSON RM fragment
/// (master04 §Raw canonical JSON: the value must carry `_type`).
#[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),
/// A TDD is not well-formed XML; the reader's error is the source.
#[error("simplified-format conversion error: TDD is not well-formed XML")]
TddXml(#[source] quick_xml::Error),
/// A TDD element carries a malformed attribute; the reader's error is the
/// source.
#[error("simplified-format conversion error: malformed TDD attribute")]
TddAttribute(#[source] quick_xml::events::attributes::AttrError),
/// A TDD element did not decode as the RM type it declares; the
/// canonical-XML reader's error is the source.
#[error("simplified-format conversion error: TDD {rm_type}")]
TddElement {
/// The RM type the element was read as.
rm_type: &'static str,
/// The canonical-XML reader's refusal.
#[source]
source: openehr_its::xml::runtime::XmlError,
},
/// 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,
},
}