Skip to main content

wip_protocol/
model.rs

1use std::collections::BTreeMap;
2
3use crate::{ProtocolError, ValidationError};
4
5/// Human-readable plain-text documentation attached to a protocol declaration.
6#[derive(Debug, Clone, PartialEq, Eq, Default)]
7pub struct Documentation {
8    /// A short, self-contained description.
9    pub summary: String,
10    /// Optional extended documentation.
11    pub details: Option<String>,
12}
13
14/// An interface identified by an exact `(scope, name)` pair in one Worldspace.
15///
16/// Neither field is normalized, case-folded, URL-decoded, or split on display
17/// delimiters. Descriptor identity is external to the descriptor itself.
18///
19/// ```
20/// use wip_protocol::{FetchInterfaceRequest, InterfaceReference};
21/// let reference = InterfaceReference {
22///     scope: "/世::界/#".into(),
23///     name: "read::書く#λ".into(),
24/// };
25/// reference.validate_for_path("/世::界/#/item")?;
26/// // Fetching is independent of an Object target path.
27/// FetchInterfaceRequest { interface: reference }.validate()?;
28/// # Ok::<(), wip_protocol::ValidationError>(())
29/// ```
30#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub struct InterfaceReference {
32    /// Canonical absolute Worldspace path of the interface's scope Object.
33    pub scope: String,
34    /// Nonempty Unicode local name, with no delimiter restrictions.
35    pub name: String,
36}
37
38impl InterfaceReference {
39    /// Validates reference shape without requiring a target Object path.
40    pub fn validate(&self) -> Result<(), ValidationError> {
41        crate::validation::validate_interface_reference(self)
42    }
43
44    /// Validates shape and that scope is an ancestor-or-self of `path`.
45    ///
46    /// This is independent of indexability. A Host maps a valid reference's
47    /// scope mismatch to `InterfaceMismatch` at the interface-selection stage,
48    /// not to the earlier shape-validation `InvalidRequest` stage.
49    pub fn validate_for_path(&self, path: &str) -> Result<(), ValidationError> {
50        crate::validation::validate_interface_scope(self, path)
51    }
52}
53
54/// The protocol projection currently published at one Worldspace path.
55///
56/// `name` is a path segment, `interfaces` is an ordered set of structured interface
57/// references, `r#ref` is optional object identity, and `validator` is an
58/// independent optional observation precondition. In particular, neither the
59/// path nor `r#ref` is a mandatory object identifier.
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct Object {
62    /// Path-segment name. The root object's name is empty.
63    pub name: String,
64    /// Short public description of the object.
65    pub description: Option<String>,
66    /// Structured interface references in host publication order.
67    pub interfaces: Vec<InterfaceReference>,
68    /// Optional opaque identity within one client-facing Worldspace.
69    pub r#ref: Option<String>,
70    /// Optional opaque validator for the object's observed state.
71    pub validator: Option<Vec<u8>>,
72}
73
74/// Transport-independent description of one interface.
75///
76/// Interface identity is supplied by interaction fields such as
77/// [`FetchInterfaceResponse::interface`], not embedded in the descriptor.
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct InterfaceDescriptor {
80    /// Descriptor format version understood by clients and hosts.
81    pub format: String,
82    /// Optional interface documentation.
83    pub documentation: Option<Documentation>,
84    /// Named declarations local to this descriptor.
85    pub types: Vec<TypeDeclaration>,
86    /// Operations exposed by this interface.
87    pub operations: Vec<OperationDeclaration>,
88}
89
90/// A descriptor-local named type declaration.
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct TypeDeclaration {
93    /// Descriptor-local type name.
94    pub name: String,
95    /// Optional type documentation.
96    pub documentation: Option<Documentation>,
97    /// Definition associated with `name`.
98    pub definition: TypeExpr,
99}
100
101/// A type expression used by declarations, fields, parameters, and returns.
102///
103/// [`TypeExpr::Named`] references are resolved only within the containing
104/// descriptor. Recursive references, including recursion through lists,
105/// records, and unions, are invalid.
106#[derive(Debug, Clone, PartialEq, Eq)]
107pub enum TypeExpr {
108    /// A value carrying no data.
109    Unit,
110    /// A boolean.
111    Boolean,
112    /// A JavaScript-lossless signed integer.
113    Integer,
114    /// A finite IEEE-754 binary64 number.
115    Number,
116    /// A Unicode string.
117    String,
118    /// An opaque byte sequence.
119    Bytes,
120    /// A schema-opaque JSON-compatible value tree.
121    Json,
122    /// A canonical absolute Worldspace path represented by a string value.
123    Entry,
124    /// A descriptor-local named declaration.
125    Named {
126        /// Referenced declaration name.
127        name: String,
128    },
129    /// A record with named fields.
130    Record {
131        /// Fields in declaration order.
132        fields: Vec<FieldDeclaration>,
133    },
134    /// An ordered list.
135    List {
136        /// Type of every list item.
137        items: Box<TypeExpr>,
138    },
139    /// A closed set of payload-free cases represented by strings.
140    Enum {
141        /// Cases in declaration order.
142        cases: Vec<EnumCase>,
143    },
144    /// A discriminated union represented by a record.
145    Union {
146        /// Cases in declaration order.
147        cases: Vec<UnionCase>,
148    },
149}
150
151/// Declaration of a record field.
152#[derive(Debug, Clone, PartialEq, Eq)]
153pub struct FieldDeclaration {
154    /// Field name.
155    pub name: String,
156    /// Whether the field must be present.
157    pub required: bool,
158    /// Optional field documentation.
159    pub documentation: Option<Documentation>,
160    /// Field value type.
161    pub r#type: TypeExpr,
162}
163
164/// One payload-free enum case.
165#[derive(Debug, Clone, PartialEq, Eq)]
166pub struct EnumCase {
167    /// Case name.
168    pub name: String,
169    /// Optional case documentation.
170    pub documentation: Option<Documentation>,
171}
172
173/// One union case with zero or one payload.
174#[derive(Debug, Clone, PartialEq, Eq)]
175pub struct UnionCase {
176    /// Case name.
177    pub name: String,
178    /// Optional case documentation.
179    pub documentation: Option<Documentation>,
180    /// Optional payload type.
181    pub payload: Option<TypeExpr>,
182}
183
184/// Declaration of one interface operation.
185#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct OperationDeclaration {
187    /// Operation name, unique within its descriptor.
188    pub name: String,
189    /// Optional operation documentation.
190    pub documentation: Option<Documentation>,
191    /// Parameters in declaration order.
192    pub parameters: Vec<ParameterDeclaration>,
193    /// Return declaration. No-data operations return [`TypeExpr::Unit`].
194    pub returns: ReturnDeclaration,
195}
196
197/// Declaration of one operation parameter.
198#[derive(Debug, Clone, PartialEq, Eq)]
199pub struct ParameterDeclaration {
200    /// Parameter name.
201    pub name: String,
202    /// Whether the parameter must be present.
203    pub required: bool,
204    /// Optional parameter documentation.
205    pub documentation: Option<Documentation>,
206    /// Parameter value type.
207    pub r#type: TypeExpr,
208}
209
210/// Declaration of an operation return value.
211#[derive(Debug, Clone, PartialEq, Eq)]
212pub struct ReturnDeclaration {
213    /// Optional return-value documentation.
214    pub documentation: Option<Documentation>,
215    /// Return value type.
216    pub r#type: TypeExpr,
217}
218
219/// A schema-neutral logical protocol value.
220///
221/// Enum cases, union cases, entries, and JSON are interpretations imposed by a
222/// descriptor. They are deliberately not variants of this enum. Record key
223/// ordering is not protocol-significant.
224#[derive(Debug, Clone, PartialEq)]
225pub enum Value {
226    /// A value carrying no data; it also represents JSON null under `Json`.
227    Unit,
228    /// A boolean.
229    Boolean(bool),
230    /// A JavaScript-lossless integer.
231    Integer(i64),
232    /// A finite IEEE-754 binary64 number.
233    Number(f64),
234    /// A Unicode string.
235    String(String),
236    /// An opaque byte sequence.
237    Bytes(Vec<u8>),
238    /// String-keyed fields with order-insensitive semantics.
239    Record(BTreeMap<String, Value>),
240    /// An ordered sequence.
241    List(Vec<Value>),
242}
243
244impl Value {
245    /// Returns the structural kind used by validation diagnostics.
246    #[must_use]
247    pub fn kind(&self) -> crate::ValueKind {
248        match self {
249            Self::Unit => crate::ValueKind::Unit,
250            Self::Boolean(_) => crate::ValueKind::Boolean,
251            Self::Integer(_) => crate::ValueKind::Integer,
252            Self::Number(_) => crate::ValueKind::Number,
253            Self::String(_) => crate::ValueKind::String,
254            Self::Bytes(_) => crate::ValueKind::Bytes,
255            Self::Record(_) => crate::ValueKind::Record,
256            Self::List(_) => crate::ValueKind::List,
257        }
258    }
259}
260
261/// Request for the object currently published at one path and its indexable descendants.
262#[derive(Debug, Clone, PartialEq, Eq)]
263pub struct ObserveRequest {
264    /// Canonical absolute path of the observation root.
265    pub path: String,
266    /// Maximum observed depth, where the requested object has depth zero.
267    pub depth: u32,
268}
269
270/// One node in an object observation.
271#[derive(Debug, Clone, PartialEq, Eq)]
272pub struct ObjectObservation {
273    /// Object observed at this Worldspace position.
274    pub object: Object,
275    /// Complete direct children when observed, or `None` at the requested depth boundary.
276    pub children: Option<Vec<ObjectObservation>>,
277}
278
279/// Successful `observe` response.
280pub type ObserveResponse = ObjectObservation;
281
282/// Request for the current descriptor of one structured interface reference.
283#[derive(Debug, Clone, PartialEq, Eq)]
284pub struct FetchInterfaceRequest {
285    /// Structured interface reference obtained from an object.
286    pub interface: InterfaceReference,
287}
288
289/// Successful `fetch_interface` response.
290#[derive(Debug, Clone, PartialEq, Eq)]
291pub struct FetchInterfaceResponse {
292    /// Structured interface reference corresponding to the request.
293    pub interface: InterfaceReference,
294    /// Optional opaque ref of the scope Object, not part of interface identity.
295    pub scope_ref: Option<String>,
296    /// Current validated descriptor.
297    pub descriptor: InterfaceDescriptor,
298    /// Optional opaque validator for this descriptor observation.
299    pub validator: Option<Vec<u8>>,
300}
301
302/// Object portion of an operation target.
303#[derive(Debug, Clone, PartialEq, Eq)]
304pub struct Target {
305    /// Canonical absolute path of the target object.
306    pub path: String,
307    /// Optional validator copied from the object observation.
308    pub validator: Option<Vec<u8>>,
309}
310
311/// Interface portion of an operation target.
312#[derive(Debug, Clone, PartialEq, Eq)]
313pub struct InterfaceTarget {
314    /// Structured reference selected from the object's interface set.
315    pub reference: InterfaceReference,
316    /// Optional scope Object ref copied from the same interface observation.
317    pub scope_ref: Option<String>,
318    /// Optional validator copied from the interface observation.
319    pub validator: Option<Vec<u8>>,
320}
321
322/// Transport-independent request to execute one operation.
323#[derive(Debug, Clone, PartialEq)]
324pub struct CallOperationRequest {
325    /// Path and optional object-state precondition.
326    pub target: Target,
327    /// Interface reference and its independent optional precondition.
328    pub interface: InterfaceTarget,
329    /// Operation name within the selected descriptor.
330    pub operation: String,
331    /// Named arguments; map iteration order has no protocol meaning.
332    pub arguments: BTreeMap<String, Value>,
333}
334
335/// Successful operation response.
336#[derive(Debug, Clone, PartialEq)]
337pub struct CallOperationResponse {
338    /// Application result validated against the operation return declaration.
339    pub result: Value,
340    /// Optional validator for the object state observed after execution.
341    pub validator: Option<Vec<u8>>,
342}
343
344/// Logical outcome of any core interaction.
345#[derive(Debug, Clone, PartialEq, Eq)]
346pub enum ProtocolResult<T> {
347    /// The interaction succeeded.
348    Success(T),
349    /// The interaction failed at the protocol boundary.
350    Failure(ProtocolError),
351}
352
353impl Object {
354    /// Validates the object-independent name and ordered-interface-set rules.
355    pub fn validate(&self) -> Result<(), ValidationError> {
356        crate::validation::validate_object(self)
357    }
358
359    /// Validates this object as the response observed at `path`.
360    pub fn validate_at_path(&self, path: &str) -> Result<(), ValidationError> {
361        crate::validation::validate_object_at_path(self, path)
362    }
363
364    /// Validates equal representations for a shared ref and present validator.
365    pub fn validate_consistency_with(&self, other: &Self) -> Result<(), ValidationError> {
366        crate::validation::validate_object_consistency(self, other)
367    }
368}
369
370impl InterfaceDescriptor {
371    /// Validates declaration structure, named references, and absence of cycles.
372    pub fn validate(&self) -> Result<(), ValidationError> {
373        crate::validation::validate_descriptor(self)
374    }
375
376    /// Validates a raw value against a type expression in this descriptor.
377    pub fn validate_value(&self, r#type: &TypeExpr, value: &Value) -> Result<(), ValidationError> {
378        crate::validation::validate_root_value(self, r#type, value)
379    }
380
381    /// Validates named arguments for one declared operation.
382    pub fn validate_arguments(
383        &self,
384        operation: &str,
385        arguments: &BTreeMap<String, Value>,
386    ) -> Result<(), ValidationError> {
387        crate::validation::validate_arguments(self, operation, arguments)
388    }
389
390    /// Validates one operation result against its return declaration.
391    pub fn validate_result(&self, operation: &str, result: &Value) -> Result<(), ValidationError> {
392        crate::validation::validate_result(self, operation, result)
393    }
394
395    /// Validates the path, operation, and arguments in a call request.
396    pub fn validate_call(&self, request: &CallOperationRequest) -> Result<(), ValidationError> {
397        crate::validation::validate_call(self, request)
398    }
399}
400
401impl ObserveRequest {
402    /// Validates the request path.
403    pub fn validate(&self) -> Result<(), ValidationError> {
404        crate::validation::validate_path(&self.path)
405    }
406
407    /// Validates observation shape, names, and requested depth.
408    ///
409    /// Failure means the Client received an invalid success response; it is not a
410    /// Host-returned [`crate::ProtocolErrorCode`].
411    pub fn validate_response(&self, response: &ObserveResponse) -> Result<(), ValidationError> {
412        crate::validation::validate_observe_response(response, self)
413    }
414}
415
416impl FetchInterfaceRequest {
417    /// Validates reference shape; fetching an interface needs no target path.
418    pub fn validate(&self) -> Result<(), ValidationError> {
419        self.interface.validate()
420    }
421}
422
423impl FetchInterfaceResponse {
424    /// Validates request correspondence and descriptor semantics.
425    ///
426    /// Failure means the Client received an invalid success response; it is not a
427    /// Host-returned [`crate::ProtocolErrorCode`]. Descriptor format support is a
428    /// separate Client concern: an otherwise valid descriptor with an unsupported
429    /// `format` is a Client-local `UnsupportedDescriptorFormat`.
430    pub fn validate_for(&self, request: &FetchInterfaceRequest) -> Result<(), ValidationError> {
431        crate::validation::validate_fetch_interface_response(self, request)
432    }
433}
434
435impl CallOperationRequest {
436    /// Validates descriptor-independent request shape only.
437    ///
438    /// Scope compatibility is deliberately deferred to interface selection so
439    /// that `NotFound` precedes `InterfaceMismatch` at the Host boundary.
440    pub fn validate(&self) -> Result<(), ValidationError> {
441        crate::validation::validate_call_shape(self)
442    }
443
444    /// Validates this call's shape and descriptor-bound operation/arguments.
445    /// Scope compatibility and membership require Host publication context.
446    pub fn validate_with(&self, descriptor: &InterfaceDescriptor) -> Result<(), ValidationError> {
447        descriptor.validate_call(self)
448    }
449}
450
451impl CallOperationResponse {
452    /// Validates this result against the named operation in `descriptor`.
453    ///
454    /// Failure means the Client received an invalid success response; it is not a
455    /// Host-returned [`crate::ProtocolErrorCode`].
456    pub fn validate_for(
457        &self,
458        descriptor: &InterfaceDescriptor,
459        operation: &str,
460    ) -> Result<(), ValidationError> {
461        descriptor.validate_result(operation, &self.result)
462    }
463}