wip_protocol/error.rs
1use std::error::Error;
2use std::fmt::{self, Display, Formatter};
3
4/// Namespace in which a declaration name must be unique.
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum NameNamespace {
7 /// Named types in an interface descriptor.
8 TypeDeclaration,
9 /// Operations in an interface descriptor.
10 Operation,
11 /// Fields in an inline or named record type.
12 Field,
13 /// Parameters in an operation declaration.
14 Parameter,
15 /// Cases in an enum type.
16 EnumCase,
17 /// Cases in a union type.
18 UnionCase,
19 /// Child object names beneath one tree node.
20 ChildObject,
21}
22
23/// Structural kind of a schema-neutral logical value.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum ValueKind {
26 /// Unit value.
27 Unit,
28 /// Boolean value.
29 Boolean,
30 /// Integer value.
31 Integer,
32 /// Floating-point number value.
33 Number,
34 /// String value.
35 String,
36 /// Byte sequence.
37 Bytes,
38 /// Record value.
39 Record,
40 /// List value.
41 List,
42}
43
44/// A typed segment identifying a declaration or value location.
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub enum PathSegment {
47 /// Descriptor root.
48 Descriptor,
49 /// Named type declaration.
50 Declaration(String),
51 /// Operation declaration or call.
52 Operation(String),
53 /// Record field declaration or value.
54 Field(String),
55 /// Operation parameter or argument.
56 Parameter(String),
57 /// Enum or union case.
58 Case(String),
59 /// List index.
60 Index(usize),
61 /// Operation return value.
62 Return,
63 /// Object at a Worldspace path.
64 Object(String),
65 /// Child object in an observation response.
66 Child(String),
67}
68
69/// Machine-readable reason why logical protocol validation failed.
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub enum ValidationErrorKind {
72 /// A name occurred more than once in one namespace.
73 DuplicateName {
74 /// Namespace containing the duplicate.
75 namespace: NameNamespace,
76 /// Duplicate name.
77 name: String,
78 },
79 /// A named type was not declared in the descriptor.
80 UnknownType {
81 /// Missing descriptor-local type name.
82 name: String,
83 },
84 /// Named declarations form a recursive cycle.
85 RecursiveType {
86 /// Cycle in traversal order, with the starting name repeated last.
87 cycle: Vec<String>,
88 },
89 /// A value has the wrong structural kind.
90 TypeMismatch {
91 /// Expected kind.
92 expected: ValueKind,
93 /// Actual kind.
94 actual: ValueKind,
95 },
96 /// A record value contains an undeclared field.
97 UnknownField {
98 /// Unknown field name.
99 name: String,
100 },
101 /// A record value omitted a required field.
102 MissingField {
103 /// Missing field name.
104 name: String,
105 },
106 /// An enum or union selected an undeclared case.
107 UnknownCase {
108 /// Unknown case name.
109 name: String,
110 },
111 /// A payload-bearing union case omitted `value`.
112 MissingUnionPayload,
113 /// A payload-free union case supplied `value`.
114 UnexpectedUnionPayload,
115 /// An operation call contains an undeclared argument.
116 UnknownParameter {
117 /// Unknown parameter name.
118 name: String,
119 },
120 /// An operation call omitted a required argument.
121 MissingParameter {
122 /// Missing parameter name.
123 name: String,
124 },
125 /// An operation is not declared by the descriptor.
126 UnknownOperation {
127 /// Unknown operation name.
128 name: String,
129 },
130 /// An integer is outside the interoperable safe range.
131 IntegerOutOfRange {
132 /// Rejected integer.
133 value: i64,
134 },
135 /// A numeric value is NaN or infinite.
136 NonFiniteNumber,
137 /// A JSON-interpreted value tree contains protocol bytes.
138 BytesInJson,
139 /// An object name is not a valid root or non-root path segment.
140 InvalidObjectName {
141 /// Rejected object name.
142 name: String,
143 },
144 /// An object's ordered interface set contains a duplicate.
145 DuplicateInterface {
146 /// Duplicate structured interface reference.
147 reference: crate::InterfaceReference,
148 },
149 /// The local interface name is empty.
150 EmptyInterfaceName,
151 /// A valid scope is not an ancestor-or-self of the target path.
152 InterfaceScopeMismatch {
153 /// Selected scope path.
154 scope: String,
155 /// Target Object path.
156 path: String,
157 },
158 /// A string is not a canonical absolute Worldspace path.
159 InvalidPath {
160 /// Rejected path.
161 path: String,
162 },
163 /// An object name does not match the path where it was observed.
164 ObjectNameMismatch {
165 /// Name required by the observation path.
166 expected: String,
167 /// Name supplied by the object.
168 actual: String,
169 },
170 /// Equivalent ref/validator observations disagree on object representation.
171 InconsistentObjectObservation {
172 /// Shared opaque object reference.
173 reference: String,
174 },
175 /// An observation omitted children before reaching the requested depth.
176 ChildrenMissingBeforeDepth {
177 /// Node depth where children were required.
178 depth: u32,
179 },
180 /// An observation included children at the requested depth boundary.
181 ChildrenAtDepthBoundary {
182 /// Boundary depth where children must be unobserved.
183 depth: u32,
184 },
185 /// A response refers to a different interface than its request.
186 InterfaceResponseMismatch {
187 /// Requested structured reference (boxed to keep validation errors small).
188 expected: Box<crate::InterfaceReference>,
189 /// Returned structured reference (boxed to keep validation errors small).
190 actual: Box<crate::InterfaceReference>,
191 },
192}
193
194/// A validation failure with a typed path to the failing location.
195#[derive(Debug, Clone, PartialEq, Eq)]
196pub struct ValidationError {
197 /// Path from the validation root to the failure.
198 pub path: Vec<PathSegment>,
199 /// Machine-readable failure reason.
200 pub kind: ValidationErrorKind,
201}
202
203impl ValidationError {
204 pub(crate) fn new(kind: ValidationErrorKind) -> Self {
205 Self {
206 path: Vec::new(),
207 kind,
208 }
209 }
210
211 pub(crate) fn prepend(mut self, segment: PathSegment) -> Self {
212 self.path.insert(0, segment);
213 self
214 }
215}
216
217impl Display for ValidationError {
218 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
219 if self.path.is_empty() {
220 write!(formatter, "protocol validation failed: {:?}", self.kind)
221 } else {
222 write!(
223 formatter,
224 "protocol validation failed at {:?}: {:?}",
225 self.path, self.kind
226 )
227 }
228 }
229}
230
231impl Error for ValidationError {}
232
233/// One core interaction whose failure codes form a closed set.
234#[derive(Debug, Clone, Copy, PartialEq, Eq)]
235pub enum ProtocolInteraction {
236 /// Observe one object and its indexable descendants to a requested depth.
237 Observe,
238 /// Fetch one interface descriptor by its structured reference.
239 FetchInterface,
240 /// Invoke one operation on a path-targeted object.
241 CallOperation,
242}
243
244/// Stable protocol failure code returned by a host.
245///
246/// This is the complete wire-level code set. A Client treats a malformed success
247/// or failure response, including a code disallowed for its interaction, as a
248/// Client-local `InvalidResponse`. An unsupported descriptor format is likewise
249/// a Client-local `UnsupportedDescriptorFormat`; neither condition is a code a
250/// Host can return.
251#[derive(Debug, Clone, Copy, PartialEq, Eq)]
252pub enum ProtocolErrorCode {
253 /// The request is structurally invalid before an operation descriptor is fixed.
254 InvalidRequest,
255 /// The target object is not published.
256 NotFound,
257 /// The interface reference is not currently published.
258 InterfaceNotFound,
259 /// The selected interface is not on the current object.
260 InterfaceMismatch,
261 /// The selected operation is not declared by the current interface.
262 OperationNotFound,
263 /// Supplied arguments do not match the fixed operation declaration.
264 InvalidArguments,
265 /// The operation requires an object validator and none was supplied.
266 ValidatorRequired,
267 /// The supplied object validator does not match current state.
268 ValidatorMismatch,
269 /// A mutable interface requires its validator and none was supplied.
270 InterfaceValidatorRequired,
271 /// The supplied interface validator does not match the descriptor.
272 InterfaceValidatorMismatch,
273 /// A published operation is not permitted for the caller.
274 PermissionDenied,
275 /// A bounded protocol resource limit was exceeded.
276 ///
277 /// For `call_operation`, the Host guarantees that the operation was not run
278 /// or that all of its effects were rolled back.
279 ResourceLimitExceeded,
280 /// The Host encountered an internal failure.
281 ///
282 /// For `call_operation`, the Host guarantees that the operation was not run
283 /// or that all of its effects were rolled back.
284 Internal,
285 /// The Host cannot guarantee that the operation was not run or rolled back.
286 ///
287 /// A Client must not automatically retry because the operation may have
288 /// committed effects even though no success response was available.
289 OperationOutcomeUnknown,
290}
291
292impl ProtocolErrorCode {
293 /// Every canonical wire-level error code.
294 pub const ALL: [Self; 14] = [
295 Self::InvalidRequest,
296 Self::NotFound,
297 Self::InterfaceNotFound,
298 Self::InterfaceMismatch,
299 Self::OperationNotFound,
300 Self::InvalidArguments,
301 Self::ValidatorRequired,
302 Self::ValidatorMismatch,
303 Self::InterfaceValidatorRequired,
304 Self::InterfaceValidatorMismatch,
305 Self::PermissionDenied,
306 Self::ResourceLimitExceeded,
307 Self::Internal,
308 Self::OperationOutcomeUnknown,
309 ];
310
311 /// Whether a Host may return this code for `interaction`.
312 ///
313 /// `false` identifies an invalid error response; it does not turn that
314 /// response into another wire-level protocol failure.
315 #[must_use]
316 pub const fn is_allowed_for(self, interaction: ProtocolInteraction) -> bool {
317 use ProtocolErrorCode::{
318 InterfaceMismatch, InterfaceNotFound, InterfaceValidatorMismatch,
319 InterfaceValidatorRequired, Internal, InvalidArguments, InvalidRequest, NotFound,
320 OperationNotFound, OperationOutcomeUnknown, PermissionDenied, ResourceLimitExceeded,
321 ValidatorMismatch, ValidatorRequired,
322 };
323 use ProtocolInteraction::{CallOperation, FetchInterface, Observe};
324
325 match (interaction, self) {
326 (_, InvalidRequest | ResourceLimitExceeded | Internal) => true,
327 (Observe, NotFound) => true,
328 (FetchInterface, InterfaceNotFound) => true,
329 (
330 CallOperation,
331 NotFound
332 | InterfaceMismatch
333 | OperationNotFound
334 | InvalidArguments
335 | ValidatorRequired
336 | ValidatorMismatch
337 | InterfaceValidatorRequired
338 | InterfaceValidatorMismatch
339 | PermissionDenied
340 | OperationOutcomeUnknown,
341 ) => true,
342 (
343 _,
344 NotFound
345 | InterfaceNotFound
346 | InterfaceMismatch
347 | OperationNotFound
348 | InvalidArguments
349 | ValidatorRequired
350 | ValidatorMismatch
351 | InterfaceValidatorRequired
352 | InterfaceValidatorMismatch
353 | PermissionDenied
354 | OperationOutcomeUnknown,
355 ) => false,
356 }
357 }
358}
359
360/// Transport-independent protocol failure.
361#[derive(Debug, Clone, PartialEq, Eq)]
362pub struct ProtocolError {
363 /// Stable programmatic code. Consumers must branch on this field.
364 pub code: ProtocolErrorCode,
365 /// Bounded human-readable diagnostic. Consumers must not branch on it.
366 pub message: String,
367}
368
369impl Display for ProtocolError {
370 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
371 write!(
372 formatter,
373 "protocol failure ({:?}): {}",
374 self.code, self.message
375 )
376 }
377}
378
379impl Error for ProtocolError {}