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 opaque interface reference.
147        reference: String,
148    },
149    /// A string is not a canonical absolute Worldspace path.
150    InvalidPath {
151        /// Rejected path.
152        path: String,
153    },
154    /// An object name does not match the path where it was observed.
155    ObjectNameMismatch {
156        /// Name required by the observation path.
157        expected: String,
158        /// Name supplied by the object.
159        actual: String,
160    },
161    /// Equivalent ref/validator observations disagree on object representation.
162    InconsistentObjectObservation {
163        /// Shared opaque object reference.
164        reference: String,
165    },
166    /// An observation omitted children before reaching the requested depth.
167    ChildrenMissingBeforeDepth {
168        /// Node depth where children were required.
169        depth: u32,
170    },
171    /// An observation included children at the requested depth boundary.
172    ChildrenAtDepthBoundary {
173        /// Boundary depth where children must be unobserved.
174        depth: u32,
175    },
176    /// A response refers to a different interface than its request.
177    InterfaceResponseMismatch {
178        /// Requested opaque reference.
179        expected: String,
180        /// Returned opaque reference.
181        actual: String,
182    },
183}
184
185/// A validation failure with a typed path to the failing location.
186#[derive(Debug, Clone, PartialEq, Eq)]
187pub struct ValidationError {
188    /// Path from the validation root to the failure.
189    pub path: Vec<PathSegment>,
190    /// Machine-readable failure reason.
191    pub kind: ValidationErrorKind,
192}
193
194impl ValidationError {
195    pub(crate) fn new(kind: ValidationErrorKind) -> Self {
196        Self {
197            path: Vec::new(),
198            kind,
199        }
200    }
201
202    pub(crate) fn prepend(mut self, segment: PathSegment) -> Self {
203        self.path.insert(0, segment);
204        self
205    }
206}
207
208impl Display for ValidationError {
209    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
210        if self.path.is_empty() {
211            write!(formatter, "protocol validation failed: {:?}", self.kind)
212        } else {
213            write!(
214                formatter,
215                "protocol validation failed at {:?}: {:?}",
216                self.path, self.kind
217            )
218        }
219    }
220}
221
222impl Error for ValidationError {}
223
224/// One core interaction whose failure codes form a closed set.
225#[derive(Debug, Clone, Copy, PartialEq, Eq)]
226pub enum ProtocolInteraction {
227    /// Observe one object and its indexable descendants to a requested depth.
228    Observe,
229    /// Fetch one interface descriptor by its opaque reference.
230    FetchInterface,
231    /// Invoke one operation on a path-targeted object.
232    CallOperation,
233}
234
235/// Stable protocol failure code returned by a host.
236///
237/// This is the complete wire-level code set. A Client treats a malformed success
238/// or failure response, including a code disallowed for its interaction, as a
239/// Client-local `InvalidResponse`. An unsupported descriptor format is likewise
240/// a Client-local `UnsupportedDescriptorFormat`; neither condition is a code a
241/// Host can return.
242#[derive(Debug, Clone, Copy, PartialEq, Eq)]
243pub enum ProtocolErrorCode {
244    /// The request is structurally invalid before an operation descriptor is fixed.
245    InvalidRequest,
246    /// The target object is not published.
247    NotFound,
248    /// The interface reference is not currently published.
249    InterfaceNotFound,
250    /// The selected interface is not on the current object.
251    InterfaceMismatch,
252    /// The selected operation is not declared by the current interface.
253    OperationNotFound,
254    /// Supplied arguments do not match the fixed operation declaration.
255    InvalidArguments,
256    /// The operation requires an object validator and none was supplied.
257    ValidatorRequired,
258    /// The supplied object validator does not match current state.
259    ValidatorMismatch,
260    /// A mutable interface requires its validator and none was supplied.
261    InterfaceValidatorRequired,
262    /// The supplied interface validator does not match the descriptor.
263    InterfaceValidatorMismatch,
264    /// A published operation is not permitted for the caller.
265    PermissionDenied,
266    /// A bounded protocol resource limit was exceeded.
267    ///
268    /// For `call_operation`, the Host guarantees that the operation was not run
269    /// or that all of its effects were rolled back.
270    ResourceLimitExceeded,
271    /// The Host encountered an internal failure.
272    ///
273    /// For `call_operation`, the Host guarantees that the operation was not run
274    /// or that all of its effects were rolled back.
275    Internal,
276    /// The Host cannot guarantee that the operation was not run or rolled back.
277    ///
278    /// A Client must not automatically retry because the operation may have
279    /// committed effects even though no success response was available.
280    OperationOutcomeUnknown,
281}
282
283impl ProtocolErrorCode {
284    /// Every canonical wire-level error code.
285    pub const ALL: [Self; 14] = [
286        Self::InvalidRequest,
287        Self::NotFound,
288        Self::InterfaceNotFound,
289        Self::InterfaceMismatch,
290        Self::OperationNotFound,
291        Self::InvalidArguments,
292        Self::ValidatorRequired,
293        Self::ValidatorMismatch,
294        Self::InterfaceValidatorRequired,
295        Self::InterfaceValidatorMismatch,
296        Self::PermissionDenied,
297        Self::ResourceLimitExceeded,
298        Self::Internal,
299        Self::OperationOutcomeUnknown,
300    ];
301
302    /// Whether a Host may return this code for `interaction`.
303    ///
304    /// `false` identifies an invalid error response; it does not turn that
305    /// response into another wire-level protocol failure.
306    #[must_use]
307    pub const fn is_allowed_for(self, interaction: ProtocolInteraction) -> bool {
308        use ProtocolErrorCode::{
309            InterfaceMismatch, InterfaceNotFound, InterfaceValidatorMismatch,
310            InterfaceValidatorRequired, Internal, InvalidArguments, InvalidRequest, NotFound,
311            OperationNotFound, OperationOutcomeUnknown, PermissionDenied, ResourceLimitExceeded,
312            ValidatorMismatch, ValidatorRequired,
313        };
314        use ProtocolInteraction::{CallOperation, FetchInterface, Observe};
315
316        match (interaction, self) {
317            (_, InvalidRequest | ResourceLimitExceeded | Internal) => true,
318            (Observe, NotFound) => true,
319            (FetchInterface, InterfaceNotFound) => true,
320            (
321                CallOperation,
322                NotFound
323                | InterfaceMismatch
324                | OperationNotFound
325                | InvalidArguments
326                | ValidatorRequired
327                | ValidatorMismatch
328                | InterfaceValidatorRequired
329                | InterfaceValidatorMismatch
330                | PermissionDenied
331                | OperationOutcomeUnknown,
332            ) => true,
333            (
334                _,
335                NotFound
336                | InterfaceNotFound
337                | InterfaceMismatch
338                | OperationNotFound
339                | InvalidArguments
340                | ValidatorRequired
341                | ValidatorMismatch
342                | InterfaceValidatorRequired
343                | InterfaceValidatorMismatch
344                | PermissionDenied
345                | OperationOutcomeUnknown,
346            ) => false,
347        }
348    }
349}
350
351/// Transport-independent protocol failure.
352#[derive(Debug, Clone, PartialEq, Eq)]
353pub struct ProtocolError {
354    /// Stable programmatic code. Consumers must branch on this field.
355    pub code: ProtocolErrorCode,
356    /// Bounded human-readable diagnostic. Consumers must not branch on it.
357    pub message: String,
358}
359
360impl Display for ProtocolError {
361    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
362        write!(
363            formatter,
364            "protocol failure ({:?}): {}",
365            self.code, self.message
366        )
367    }
368}
369
370impl Error for ProtocolError {}