Skip to main content

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 {}