fix: align protocol model with canonical WIP
This commit is contained in:
@@ -8,79 +8,65 @@ pub enum NameNamespace {
|
||||
TypeDeclaration,
|
||||
/// Operations in an interface descriptor.
|
||||
Operation,
|
||||
/// Fields in a record declaration.
|
||||
/// Fields in an inline or named record type.
|
||||
Field,
|
||||
/// Parameters in an operation declaration.
|
||||
Parameter,
|
||||
/// Variants in an enum declaration.
|
||||
EnumVariant,
|
||||
/// Variants in a union declaration.
|
||||
UnionVariant,
|
||||
/// Fields supplied in a record value.
|
||||
RecordValueField,
|
||||
/// Arguments supplied in an operation call.
|
||||
Argument,
|
||||
/// Members in a logical JSON object.
|
||||
JsonMember,
|
||||
/// Descriptors in an interface observation.
|
||||
InterfaceDescriptor,
|
||||
/// Cases in an enum type.
|
||||
EnumCase,
|
||||
/// Cases in a union type.
|
||||
UnionCase,
|
||||
/// Child object names beneath one tree node.
|
||||
ChildObject,
|
||||
}
|
||||
|
||||
/// Structural kind of a logical protocol value.
|
||||
/// Structural kind of a schema-neutral logical value.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ValueKind {
|
||||
/// String value.
|
||||
String,
|
||||
/// Unit value.
|
||||
Unit,
|
||||
/// Boolean value.
|
||||
Boolean,
|
||||
/// Integer value.
|
||||
Integer,
|
||||
/// Floating-point number value.
|
||||
Number,
|
||||
/// String value.
|
||||
String,
|
||||
/// Byte sequence.
|
||||
Bytes,
|
||||
/// List value.
|
||||
List,
|
||||
/// Key/value entry.
|
||||
Entry,
|
||||
/// Record value.
|
||||
Record,
|
||||
/// Enum value.
|
||||
Enum,
|
||||
/// Tagged-union value.
|
||||
Union,
|
||||
/// Logical JSON value.
|
||||
Json,
|
||||
/// List value.
|
||||
List,
|
||||
}
|
||||
|
||||
/// A typed segment identifying a declaration or value location.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PathSegment {
|
||||
/// Interface descriptor.
|
||||
Interface(String),
|
||||
/// Descriptor root.
|
||||
Descriptor,
|
||||
/// Named type declaration.
|
||||
Declaration(String),
|
||||
/// Operation declaration or call.
|
||||
Operation(String),
|
||||
/// Record field declaration or value.
|
||||
Field(String),
|
||||
/// Operation parameter declaration or argument.
|
||||
/// Operation parameter or argument.
|
||||
Parameter(String),
|
||||
/// Enum or union variant.
|
||||
Variant(String),
|
||||
/// List or JSON array index.
|
||||
/// Enum or union case.
|
||||
Case(String),
|
||||
/// List index.
|
||||
Index(usize),
|
||||
/// Entry key.
|
||||
EntryKey,
|
||||
/// Entry value.
|
||||
EntryValue,
|
||||
/// Operation return value.
|
||||
Return,
|
||||
/// JSON object member.
|
||||
JsonMember(String),
|
||||
/// Object at a Worldspace path.
|
||||
Object(String),
|
||||
/// Child object in a tree response.
|
||||
Child(String),
|
||||
}
|
||||
|
||||
/// Machine-readable reason why protocol validation failed.
|
||||
/// Machine-readable reason why logical protocol validation failed.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ValidationErrorKind {
|
||||
/// A name occurred more than once in one namespace.
|
||||
@@ -117,11 +103,15 @@ pub enum ValidationErrorKind {
|
||||
/// Missing field name.
|
||||
name: String,
|
||||
},
|
||||
/// An enum or union value selected an undeclared variant.
|
||||
UnknownVariant {
|
||||
/// Unknown variant name.
|
||||
/// An enum or union selected an undeclared case.
|
||||
UnknownCase {
|
||||
/// Unknown case name.
|
||||
name: String,
|
||||
},
|
||||
/// A payload-bearing union case omitted `value`.
|
||||
MissingUnionPayload,
|
||||
/// A payload-free union case supplied `value`.
|
||||
UnexpectedUnionPayload,
|
||||
/// An operation call contains an undeclared argument.
|
||||
UnknownParameter {
|
||||
/// Unknown parameter name.
|
||||
@@ -132,44 +122,66 @@ pub enum ValidationErrorKind {
|
||||
/// Missing parameter name.
|
||||
name: String,
|
||||
},
|
||||
/// An integer is outside the JavaScript-lossless range.
|
||||
/// An operation is not declared by the descriptor.
|
||||
UnknownOperation {
|
||||
/// Unknown operation name.
|
||||
name: String,
|
||||
},
|
||||
/// An integer is outside the interoperable safe range.
|
||||
IntegerOutOfRange {
|
||||
/// Rejected integer.
|
||||
value: i64,
|
||||
},
|
||||
/// A numeric value is NaN or infinite.
|
||||
NonFiniteNumber,
|
||||
/// A JSON-interpreted value tree contains protocol bytes.
|
||||
BytesInJson,
|
||||
/// An object name is not a valid root or non-root path segment.
|
||||
InvalidObjectName {
|
||||
/// Rejected object name.
|
||||
name: String,
|
||||
},
|
||||
/// An object's ordered interface set contains a duplicate.
|
||||
DuplicateInterface {
|
||||
/// Duplicate interface name.
|
||||
name: String,
|
||||
/// Duplicate opaque interface reference.
|
||||
reference: String,
|
||||
},
|
||||
/// An observation omits a descriptor named by its object.
|
||||
MissingInterfaceDescriptor {
|
||||
/// Missing interface name.
|
||||
name: String,
|
||||
/// A string is not a canonical absolute Worldspace path.
|
||||
InvalidPath {
|
||||
/// Rejected path.
|
||||
path: String,
|
||||
},
|
||||
/// An observation contains a descriptor not named by its object.
|
||||
UnexpectedInterfaceDescriptor {
|
||||
/// Unexpected interface name.
|
||||
name: String,
|
||||
},
|
||||
/// A call targets an interface other than the descriptor being used.
|
||||
InterfaceMismatch {
|
||||
/// Descriptor interface name.
|
||||
/// An object name does not match the path where it was observed.
|
||||
ObjectNameMismatch {
|
||||
/// Name required by the observation path.
|
||||
expected: String,
|
||||
/// Call target interface name.
|
||||
/// Name supplied by the object.
|
||||
actual: String,
|
||||
},
|
||||
/// An operation is not declared by the interface.
|
||||
UnknownOperation {
|
||||
/// Unknown operation name.
|
||||
name: String,
|
||||
/// Equivalent ref/validator observations disagree on object representation.
|
||||
InconsistentObjectObservation {
|
||||
/// Shared opaque object reference.
|
||||
reference: String,
|
||||
},
|
||||
/// A `fetch_tree` request used a zero limit.
|
||||
ZeroTreeLimit,
|
||||
/// A tree response exceeds the requested maximum depth.
|
||||
TreeDepthExceeded {
|
||||
/// Requested maximum depth.
|
||||
maximum: u32,
|
||||
},
|
||||
/// A tree response exceeds the requested maximum node count.
|
||||
TreeLimitExceeded {
|
||||
/// Requested maximum node count.
|
||||
limit: u32,
|
||||
},
|
||||
/// A response refers to a different interface than its request.
|
||||
InterfaceResponseMismatch {
|
||||
/// Requested opaque reference.
|
||||
expected: String,
|
||||
/// Returned opaque reference.
|
||||
actual: String,
|
||||
},
|
||||
/// A no-return operation produced a value.
|
||||
UnexpectedReturnValue,
|
||||
/// An operation with a return declaration produced no value.
|
||||
MissingReturnValue,
|
||||
}
|
||||
|
||||
/// A validation failure with a typed path to the failing location.
|
||||
@@ -211,30 +223,44 @@ impl Display for ValidationError {
|
||||
|
||||
impl Error for ValidationError {}
|
||||
|
||||
/// Stable category of an error returned by a protocol host runtime.
|
||||
/// Stable protocol failure code returned by a host or produced by a client.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ProtocolErrorCategory {
|
||||
pub enum ProtocolErrorCode {
|
||||
/// The request is structurally invalid.
|
||||
InvalidRequest,
|
||||
/// The target object is not published.
|
||||
ObjectNotFound,
|
||||
/// The target interface is not implemented by the object.
|
||||
NotFound,
|
||||
/// The interface reference is not currently published.
|
||||
InterfaceNotFound,
|
||||
/// The target operation is not declared by the interface.
|
||||
/// The selected interface is not on the current object.
|
||||
InterfaceMismatch,
|
||||
/// The selected operation is not declared by the current interface.
|
||||
OperationNotFound,
|
||||
/// Supplied operation arguments are invalid.
|
||||
InvalidArguments,
|
||||
/// The implementation rejected the call for a domain reason.
|
||||
OperationFailed,
|
||||
/// A host result does not satisfy the declaration.
|
||||
InvalidResult,
|
||||
/// The operation requires an object validator and none was supplied.
|
||||
ValidatorRequired,
|
||||
/// The supplied object validator does not match current state.
|
||||
ValidatorMismatch,
|
||||
/// A mutable interface requires its validator and none was supplied.
|
||||
InterfaceValidatorRequired,
|
||||
/// The supplied interface validator does not match the descriptor.
|
||||
InterfaceValidatorMismatch,
|
||||
/// A published operation is not permitted for the caller.
|
||||
PermissionDenied,
|
||||
/// A bounded protocol resource limit was exceeded.
|
||||
ResourceLimitExceeded,
|
||||
/// The host encountered an internal failure.
|
||||
Internal,
|
||||
}
|
||||
|
||||
/// Transport-independent protocol failure returned by a host runtime.
|
||||
/// Transport-independent protocol failure.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ProtocolError {
|
||||
/// Stable programmatic category.
|
||||
pub category: ProtocolErrorCategory,
|
||||
/// Human-readable diagnostic. Consumers must not branch on this text.
|
||||
/// Stable programmatic code. Consumers must branch on this field.
|
||||
pub code: ProtocolErrorCode,
|
||||
/// Bounded human-readable diagnostic. Consumers must not branch on it.
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
//! Transport-independent logical types for the Web Interface Protocol (WIP).
|
||||
//! Canonical transport-independent logical types for the Web Interface Protocol.
|
||||
//!
|
||||
//! This crate deliberately models interface declarations, published objects,
|
||||
//! operation calls, and operation results without assigning them HTTP routes,
|
||||
//! methods, headers, or JSON envelopes. A transport can encode these values,
|
||||
//! but the encoding is outside this crate's contract.
|
||||
//! This crate follows the Core Model and `3-protocol.md` without assigning HTTP
|
||||
//! routes, methods, headers, URL encodings, or JSON envelopes. The correspondence
|
||||
//! between canonical logical concepts and this crate is direct:
|
||||
//!
|
||||
//! | Canonical protocol concept | Rust API |
|
||||
//! | --- | --- |
|
||||
//! | Object projection and ordered interface set | [`Object`] |
|
||||
//! | Optional object identity / state precondition | `Object::ref` / [`Object::validator`] |
|
||||
//! | Interface Descriptor | [`InterfaceDescriptor`] |
|
||||
//! | Named declaration and all schema expressions | [`TypeDeclaration`] / [`TypeExpr`] |
|
||||
//! | Schema-neutral primitive/composite value | [`Value`] |
|
||||
//! | `fetch` | [`FetchRequest`] / [`FetchResponse`] |
|
||||
//! | `fetch_tree` | [`FetchTreeRequest`] / [`FetchTreeResponse`] |
|
||||
//! | `fetch_interface` | [`FetchInterfaceRequest`] / [`FetchInterfaceResponse`] |
|
||||
//! | Object and interface call preconditions | [`Target`] / [`InterfaceTarget`] |
|
||||
//! | `call_operation` | [`CallOperationRequest`] / [`CallOperationResponse`] |
|
||||
//! | Stable failure taxonomy | [`ProtocolErrorCode`] / [`ProtocolError`] |
|
||||
//!
|
||||
//! Paths, object refs, interface refs, and validators remain separate concepts.
|
||||
//! They are represented by ordinary protocol strings/bytes in the corresponding
|
||||
//! fields. In particular, object refs are optional, operation targeting uses a
|
||||
//! path, and this crate defines no interaction that fetches an object by ref.
|
||||
//!
|
||||
//! [`Value`] contains only the canonical raw value kinds. A descriptor interprets
|
||||
//! enum values as strings, entries as canonical-path strings, and unions as
|
||||
//! records with a `$case` string plus an optional `value` payload. JSON uses the
|
||||
//! same value tree (`Unit` is JSON null) and rejects bytes.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
@@ -12,19 +35,33 @@ mod model;
|
||||
mod validation;
|
||||
|
||||
pub use error::{
|
||||
NameNamespace, PathSegment, ProtocolError, ProtocolErrorCategory, ValidationError,
|
||||
NameNamespace, PathSegment, ProtocolError, ProtocolErrorCode, ValidationError,
|
||||
ValidationErrorKind, ValueKind,
|
||||
};
|
||||
pub use model::{
|
||||
Documentation, EnumDeclaration, EnumVariantDeclaration, FieldDeclaration, InterfaceDescriptor,
|
||||
InterfaceObservation, JsonMember, JsonValue, NamedValue, Object, ObjectId, OperationCall,
|
||||
OperationDeclaration, OperationOutcome, OperationResult, OperationTarget, ParameterDeclaration,
|
||||
RecordDeclaration, ReturnDeclaration, TypeDeclaration, TypeExpr, UnionDeclaration,
|
||||
UnionVariantDeclaration, Value,
|
||||
CallOperationRequest, CallOperationResponse, Documentation, EnumCase, FetchInterfaceRequest,
|
||||
FetchInterfaceResponse, FetchRequest, FetchResponse, FetchTreeRequest, FetchTreeResponse,
|
||||
FieldDeclaration, InterfaceDescriptor, InterfaceTarget, Object, ObjectTreeNode,
|
||||
OperationDeclaration, ParameterDeclaration, ProtocolResult, ReturnDeclaration, Target,
|
||||
TypeDeclaration, TypeExpr, UnionCase, Value,
|
||||
};
|
||||
|
||||
/// Largest integer that is exactly interoperable with a JavaScript `Number`.
|
||||
/// Validates a canonical absolute Worldspace path.
|
||||
pub fn validate_path(path: &str) -> Result<(), ValidationError> {
|
||||
validation::validate_path(path)
|
||||
}
|
||||
|
||||
/// Canonical format identifier for the first Interface Descriptor format.
|
||||
pub const INTERFACE_FORMAT_V1: &str = "wip-interface/1";
|
||||
|
||||
/// Discriminator field used by a union's raw record value.
|
||||
pub const UNION_CASE_FIELD: &str = "$case";
|
||||
|
||||
/// Optional payload field used by a union's raw record value.
|
||||
pub const UNION_VALUE_FIELD: &str = "value";
|
||||
|
||||
/// Largest integer exactly interoperable with a JavaScript `Number`.
|
||||
pub const MAX_SAFE_INTEGER: i64 = 9_007_199_254_740_991;
|
||||
|
||||
/// Smallest integer that is exactly interoperable with a JavaScript `Number`.
|
||||
/// Smallest integer exactly interoperable with a JavaScript `Number`.
|
||||
pub const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER;
|
||||
|
||||
+292
-251
@@ -1,6 +1,8 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::{ProtocolError, ValidationError};
|
||||
|
||||
/// Human-readable documentation attached to a protocol declaration.
|
||||
/// Human-readable plain-text documentation attached to a protocol declaration.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct Documentation {
|
||||
/// A short, self-contained description.
|
||||
@@ -9,242 +11,194 @@ pub struct Documentation {
|
||||
pub details: Option<String>,
|
||||
}
|
||||
|
||||
/// Stable logical identity of a published object.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ObjectId(pub String);
|
||||
|
||||
/// A published object and the ordered set of interfaces it implements.
|
||||
/// The protocol projection currently published at one Worldspace path.
|
||||
///
|
||||
/// The vector order is significant. Call [`Object::validate`] before
|
||||
/// publication to reject duplicate interface names.
|
||||
/// `name` is a path segment, `interfaces` is an ordered set of opaque interface
|
||||
/// references, `r#ref` is optional object identity, and `validator` is an
|
||||
/// independent optional observation precondition. In particular, neither the
|
||||
/// path nor `r#ref` is a mandatory object identifier.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Object {
|
||||
/// Logical object identity.
|
||||
pub id: ObjectId,
|
||||
/// Interface names in publication order.
|
||||
/// Path-segment name. The root object's name is empty.
|
||||
pub name: String,
|
||||
/// Short public description of the object.
|
||||
pub description: Option<String>,
|
||||
/// Opaque interface references in host publication order.
|
||||
pub interfaces: Vec<String>,
|
||||
}
|
||||
|
||||
/// A complete logical observation of an object and its interface descriptors.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct InterfaceObservation {
|
||||
/// Observed object.
|
||||
pub object: Object,
|
||||
/// Descriptors corresponding to `object.interfaces`.
|
||||
pub descriptors: Vec<InterfaceDescriptor>,
|
||||
/// Optional opaque identity within one client-facing Worldspace.
|
||||
pub r#ref: Option<String>,
|
||||
/// Optional opaque validator for the object's observed state.
|
||||
pub validator: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Transport-independent description of one interface.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
///
|
||||
/// Interface identity is supplied by interaction fields such as
|
||||
/// [`FetchInterfaceResponse::interface`], not embedded in the descriptor.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InterfaceDescriptor {
|
||||
/// Interface name.
|
||||
pub name: String,
|
||||
/// Descriptor format version understood by clients and hosts.
|
||||
pub format: String,
|
||||
/// Optional interface documentation.
|
||||
pub documentation: Option<Documentation>,
|
||||
/// Named type declarations local to this descriptor.
|
||||
pub declarations: Vec<TypeDeclaration>,
|
||||
/// Named declarations local to this descriptor.
|
||||
pub types: Vec<TypeDeclaration>,
|
||||
/// Operations exposed by this interface.
|
||||
pub operations: Vec<OperationDeclaration>,
|
||||
}
|
||||
|
||||
/// A named type declaration local to an interface descriptor.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TypeDeclaration {
|
||||
/// A record declaration.
|
||||
Record(RecordDeclaration),
|
||||
/// An enumeration declaration.
|
||||
Enum(EnumDeclaration),
|
||||
/// A tagged union declaration.
|
||||
Union(UnionDeclaration),
|
||||
}
|
||||
|
||||
impl TypeDeclaration {
|
||||
/// Returns the descriptor-local declaration name.
|
||||
#[must_use]
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Self::Record(declaration) => &declaration.name,
|
||||
Self::Enum(declaration) => &declaration.name,
|
||||
Self::Union(declaration) => &declaration.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Declaration of a record with named fields.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct RecordDeclaration {
|
||||
/// A descriptor-local named type declaration.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypeDeclaration {
|
||||
/// Descriptor-local type name.
|
||||
pub name: String,
|
||||
/// Optional record documentation.
|
||||
/// Optional type documentation.
|
||||
pub documentation: Option<Documentation>,
|
||||
/// Definition associated with `name`.
|
||||
pub definition: TypeExpr,
|
||||
}
|
||||
|
||||
/// A type expression used by declarations, fields, parameters, and returns.
|
||||
///
|
||||
/// [`TypeExpr::Named`] references are resolved only within the containing
|
||||
/// descriptor. Recursive references, including recursion through lists,
|
||||
/// records, and unions, are invalid.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum TypeExpr {
|
||||
/// A value carrying no data.
|
||||
Unit,
|
||||
/// A boolean.
|
||||
Boolean,
|
||||
/// A JavaScript-lossless signed integer.
|
||||
Integer,
|
||||
/// A finite IEEE-754 binary64 number.
|
||||
Number,
|
||||
/// A Unicode string.
|
||||
String,
|
||||
/// An opaque byte sequence.
|
||||
Bytes,
|
||||
/// A schema-opaque JSON-compatible value tree.
|
||||
Json,
|
||||
/// A canonical absolute Worldspace path represented by a string value.
|
||||
Entry,
|
||||
/// A descriptor-local named declaration.
|
||||
Named {
|
||||
/// Referenced declaration name.
|
||||
name: String,
|
||||
},
|
||||
/// A record with named fields.
|
||||
Record {
|
||||
/// Fields in declaration order.
|
||||
pub fields: Vec<FieldDeclaration>,
|
||||
fields: Vec<FieldDeclaration>,
|
||||
},
|
||||
/// An ordered list.
|
||||
List {
|
||||
/// Type of every list item.
|
||||
items: Box<TypeExpr>,
|
||||
},
|
||||
/// A closed set of payload-free cases represented by strings.
|
||||
Enum {
|
||||
/// Cases in declaration order.
|
||||
cases: Vec<EnumCase>,
|
||||
},
|
||||
/// A discriminated union represented by a record.
|
||||
Union {
|
||||
/// Cases in declaration order.
|
||||
cases: Vec<UnionCase>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Declaration of a record field.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FieldDeclaration {
|
||||
/// Field name.
|
||||
pub name: String,
|
||||
/// Whether the field must be present.
|
||||
pub required: bool,
|
||||
/// Optional field documentation.
|
||||
pub documentation: Option<Documentation>,
|
||||
/// Field value type.
|
||||
pub ty: TypeExpr,
|
||||
/// Whether the field may be omitted.
|
||||
///
|
||||
/// Optionality represents absence, not a `null` value.
|
||||
pub optional: bool,
|
||||
pub r#type: TypeExpr,
|
||||
}
|
||||
|
||||
/// Declaration of an enumeration.
|
||||
/// One payload-free enum case.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EnumDeclaration {
|
||||
/// Descriptor-local type name.
|
||||
pub struct EnumCase {
|
||||
/// Case name.
|
||||
pub name: String,
|
||||
/// Optional enum documentation.
|
||||
/// Optional case documentation.
|
||||
pub documentation: Option<Documentation>,
|
||||
/// Allowed variants in declaration order.
|
||||
pub variants: Vec<EnumVariantDeclaration>,
|
||||
}
|
||||
|
||||
/// Declaration of one enumeration variant.
|
||||
/// One union case with zero or one payload.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EnumVariantDeclaration {
|
||||
/// Variant name.
|
||||
pub struct UnionCase {
|
||||
/// Case name.
|
||||
pub name: String,
|
||||
/// Optional variant documentation.
|
||||
/// Optional case documentation.
|
||||
pub documentation: Option<Documentation>,
|
||||
}
|
||||
|
||||
/// Declaration of a tagged union.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct UnionDeclaration {
|
||||
/// Descriptor-local type name.
|
||||
pub name: String,
|
||||
/// Optional union documentation.
|
||||
pub documentation: Option<Documentation>,
|
||||
/// Allowed tagged variants in declaration order.
|
||||
pub variants: Vec<UnionVariantDeclaration>,
|
||||
}
|
||||
|
||||
/// Declaration of one tagged-union variant.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct UnionVariantDeclaration {
|
||||
/// Variant tag.
|
||||
pub name: String,
|
||||
/// Optional variant documentation.
|
||||
pub documentation: Option<Documentation>,
|
||||
/// Payload type.
|
||||
pub ty: TypeExpr,
|
||||
}
|
||||
|
||||
/// A type expression used by fields, parameters, returns, and union payloads.
|
||||
///
|
||||
/// `Named` references are resolved only within the containing descriptor.
|
||||
/// Optional values are represented by the containing field or parameter and
|
||||
/// therefore are intentionally not a type expression.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TypeExpr {
|
||||
/// A UTF-8 string.
|
||||
String,
|
||||
/// A boolean.
|
||||
Boolean,
|
||||
/// A JavaScript-lossless integer.
|
||||
Integer,
|
||||
/// A finite IEEE-754 double precision number.
|
||||
Number,
|
||||
/// An opaque byte sequence.
|
||||
Bytes,
|
||||
/// An ordered sequence of values.
|
||||
List(Box<TypeExpr>),
|
||||
/// A key/value entry.
|
||||
Entry {
|
||||
/// Entry key type.
|
||||
key: Box<TypeExpr>,
|
||||
/// Entry value type.
|
||||
value: Box<TypeExpr>,
|
||||
},
|
||||
/// A logical JSON value.
|
||||
Json,
|
||||
/// A descriptor-local named declaration.
|
||||
Named(String),
|
||||
/// Optional payload type.
|
||||
pub payload: Option<TypeExpr>,
|
||||
}
|
||||
|
||||
/// Declaration of one interface operation.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct OperationDeclaration {
|
||||
/// Operation name, unique within its interface.
|
||||
/// Operation name, unique within its descriptor.
|
||||
pub name: String,
|
||||
/// Optional operation documentation.
|
||||
pub documentation: Option<Documentation>,
|
||||
/// Parameters in declaration order.
|
||||
pub parameters: Vec<ParameterDeclaration>,
|
||||
/// Return declaration, or `None` for an operation with no return value.
|
||||
pub returns: Option<ReturnDeclaration>,
|
||||
/// Return declaration. No-data operations return [`TypeExpr::Unit`].
|
||||
pub returns: ReturnDeclaration,
|
||||
}
|
||||
|
||||
/// Declaration of one operation parameter.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ParameterDeclaration {
|
||||
/// Parameter name.
|
||||
pub name: String,
|
||||
/// Whether the parameter must be present.
|
||||
pub required: bool,
|
||||
/// Optional parameter documentation.
|
||||
pub documentation: Option<Documentation>,
|
||||
/// Parameter value type.
|
||||
pub ty: TypeExpr,
|
||||
/// Whether the parameter may be omitted.
|
||||
///
|
||||
/// Optionality represents absence, not a `null` value.
|
||||
pub optional: bool,
|
||||
pub r#type: TypeExpr,
|
||||
}
|
||||
|
||||
/// Declaration of an operation return value.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ReturnDeclaration {
|
||||
/// Optional return-value documentation.
|
||||
pub documentation: Option<Documentation>,
|
||||
/// Return value type.
|
||||
pub ty: TypeExpr,
|
||||
pub r#type: TypeExpr,
|
||||
}
|
||||
|
||||
/// A logical protocol value.
|
||||
/// A schema-neutral logical protocol value.
|
||||
///
|
||||
/// There is no general `Null` variant: optional record fields and operation
|
||||
/// parameters are omitted. JSON null remains available inside [`Value::Json`].
|
||||
/// Enum cases, union cases, entries, and JSON are interpretations imposed by a
|
||||
/// descriptor. They are deliberately not variants of this enum. Record key
|
||||
/// ordering is not protocol-significant.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Value {
|
||||
/// A UTF-8 string.
|
||||
String(String),
|
||||
/// A value carrying no data; it also represents JSON null under `Json`.
|
||||
Unit,
|
||||
/// A boolean.
|
||||
Boolean(bool),
|
||||
/// An integer, validated against the JavaScript safe integer range.
|
||||
/// A JavaScript-lossless integer.
|
||||
Integer(i64),
|
||||
/// A number, validated to be finite.
|
||||
/// A finite IEEE-754 binary64 number.
|
||||
Number(f64),
|
||||
/// A Unicode string.
|
||||
String(String),
|
||||
/// An opaque byte sequence.
|
||||
Bytes(Vec<u8>),
|
||||
/// String-keyed fields with order-insensitive semantics.
|
||||
Record(BTreeMap<String, Value>),
|
||||
/// An ordered sequence.
|
||||
List(Vec<Value>),
|
||||
/// A key/value entry.
|
||||
Entry {
|
||||
/// Entry key.
|
||||
key: Box<Value>,
|
||||
/// Entry value.
|
||||
value: Box<Value>,
|
||||
},
|
||||
/// Record fields. Duplicate names are invalid.
|
||||
Record(Vec<NamedValue>),
|
||||
/// An enum variant name.
|
||||
Enum(String),
|
||||
/// A tagged-union value.
|
||||
Union {
|
||||
/// Selected variant tag.
|
||||
variant: String,
|
||||
/// Variant payload.
|
||||
value: Box<Value>,
|
||||
},
|
||||
/// A logical JSON value.
|
||||
Json(JsonValue),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
@@ -252,132 +206,219 @@ impl Value {
|
||||
#[must_use]
|
||||
pub fn kind(&self) -> crate::ValueKind {
|
||||
match self {
|
||||
Self::String(_) => crate::ValueKind::String,
|
||||
Self::Unit => crate::ValueKind::Unit,
|
||||
Self::Boolean(_) => crate::ValueKind::Boolean,
|
||||
Self::Integer(_) => crate::ValueKind::Integer,
|
||||
Self::Number(_) => crate::ValueKind::Number,
|
||||
Self::String(_) => crate::ValueKind::String,
|
||||
Self::Bytes(_) => crate::ValueKind::Bytes,
|
||||
Self::List(_) => crate::ValueKind::List,
|
||||
Self::Entry { .. } => crate::ValueKind::Entry,
|
||||
Self::Record(_) => crate::ValueKind::Record,
|
||||
Self::Enum(_) => crate::ValueKind::Enum,
|
||||
Self::Union { .. } => crate::ValueKind::Union,
|
||||
Self::Json(_) => crate::ValueKind::Json,
|
||||
Self::List(_) => crate::ValueKind::List,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A named value used by records and operation arguments.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct NamedValue {
|
||||
/// Field or parameter name.
|
||||
pub name: String,
|
||||
/// Associated value.
|
||||
pub value: Value,
|
||||
}
|
||||
|
||||
/// A transport-independent JSON value.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum JsonValue {
|
||||
/// JSON null.
|
||||
Null,
|
||||
/// JSON boolean.
|
||||
Boolean(bool),
|
||||
/// JSON integer, validated against the JavaScript safe integer range.
|
||||
Integer(i64),
|
||||
/// JSON number, validated to be finite.
|
||||
Number(f64),
|
||||
/// JSON string.
|
||||
String(String),
|
||||
/// JSON array.
|
||||
Array(Vec<JsonValue>),
|
||||
/// JSON object. Duplicate member names are invalid.
|
||||
Object(Vec<JsonMember>),
|
||||
}
|
||||
|
||||
/// A member of a logical JSON object.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct JsonMember {
|
||||
/// Member name.
|
||||
pub name: String,
|
||||
/// Member value.
|
||||
pub value: JsonValue,
|
||||
}
|
||||
|
||||
/// Logical destination of an operation call.
|
||||
/// Request for the object currently published at one path.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct OperationTarget {
|
||||
/// Target object.
|
||||
pub object: ObjectId,
|
||||
/// Target interface name.
|
||||
pub struct FetchRequest {
|
||||
/// Canonical absolute Worldspace path.
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
/// Successful `fetch` response.
|
||||
pub type FetchResponse = Object;
|
||||
|
||||
/// Request for a bounded breadth-first object-tree observation.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FetchTreeRequest {
|
||||
/// Canonical absolute path of the tree root.
|
||||
pub path: String,
|
||||
/// Maximum depth, where the requested object has depth zero.
|
||||
pub depth: u32,
|
||||
/// Maximum total node count, including the root. Must be nonzero.
|
||||
pub limit: u32,
|
||||
}
|
||||
|
||||
/// One node in a tree observation.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ObjectTreeNode {
|
||||
/// Object observed at this tree position.
|
||||
pub object: Object,
|
||||
/// Direct children in host-defined traversal order.
|
||||
pub children: Vec<ObjectTreeNode>,
|
||||
}
|
||||
|
||||
/// Successful `fetch_tree` response.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FetchTreeResponse {
|
||||
/// Observed tree rooted at the requested path.
|
||||
pub root: ObjectTreeNode,
|
||||
/// Whether public indexable children were omitted by depth or limit.
|
||||
pub truncated: bool,
|
||||
}
|
||||
|
||||
/// Request for the current descriptor of one opaque interface reference.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FetchInterfaceRequest {
|
||||
/// Opaque interface reference obtained from an object.
|
||||
pub interface: String,
|
||||
/// Target operation name.
|
||||
}
|
||||
|
||||
/// Successful `fetch_interface` response.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FetchInterfaceResponse {
|
||||
/// Opaque interface reference corresponding to the request.
|
||||
pub interface: String,
|
||||
/// Current validated descriptor.
|
||||
pub descriptor: InterfaceDescriptor,
|
||||
/// Optional opaque validator for this descriptor observation.
|
||||
pub validator: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Object portion of an operation target.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Target {
|
||||
/// Canonical absolute path of the target object.
|
||||
pub path: String,
|
||||
/// Optional validator copied from the object observation.
|
||||
pub validator: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Interface portion of an operation target.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InterfaceTarget {
|
||||
/// Opaque interface reference selected from the object's interface set.
|
||||
pub reference: String,
|
||||
/// Optional validator copied from the interface observation.
|
||||
pub validator: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Transport-independent request to execute one operation.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct CallOperationRequest {
|
||||
/// Path and optional object-state precondition.
|
||||
pub target: Target,
|
||||
/// Interface reference and its independent optional precondition.
|
||||
pub interface: InterfaceTarget,
|
||||
/// Operation name within the selected descriptor.
|
||||
pub operation: String,
|
||||
/// Named arguments; map iteration order has no protocol meaning.
|
||||
pub arguments: BTreeMap<String, Value>,
|
||||
}
|
||||
|
||||
/// A transport-independent operation call.
|
||||
/// Successful operation response.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct OperationCall {
|
||||
/// Logical call target.
|
||||
pub target: OperationTarget,
|
||||
/// Named arguments. Optional arguments are represented by omission.
|
||||
pub arguments: Vec<NamedValue>,
|
||||
pub struct CallOperationResponse {
|
||||
/// Application result validated against the operation return declaration.
|
||||
pub result: Value,
|
||||
/// Optional validator for the object state observed after execution.
|
||||
pub validator: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Successful or failed logical outcome of an operation.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum OperationOutcome {
|
||||
/// Operation returned. `None` represents a declared no-return operation.
|
||||
Returned(Option<Value>),
|
||||
/// Operation failed with a protocol-level error.
|
||||
Failed(ProtocolError),
|
||||
}
|
||||
|
||||
/// A transport-independent operation result.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct OperationResult {
|
||||
/// Target corresponding to the result.
|
||||
pub target: OperationTarget,
|
||||
/// Logical outcome.
|
||||
pub outcome: OperationOutcome,
|
||||
/// Logical outcome of any core interaction.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ProtocolResult<T> {
|
||||
/// The interaction succeeded.
|
||||
Success(T),
|
||||
/// The interaction failed at the protocol boundary.
|
||||
Failure(ProtocolError),
|
||||
}
|
||||
|
||||
impl Object {
|
||||
/// Validates ordered-set interface semantics.
|
||||
/// Validates the object-independent name and ordered-interface-set rules.
|
||||
pub fn validate(&self) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_object(self)
|
||||
}
|
||||
|
||||
/// Validates this object as the response observed at `path`.
|
||||
pub fn validate_at_path(&self, path: &str) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_object_at_path(self, path)
|
||||
}
|
||||
|
||||
impl InterfaceObservation {
|
||||
/// Validates the object, descriptor structure, and their correspondence.
|
||||
pub fn validate(&self) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_observation(self)
|
||||
/// Validates equal representations for a shared ref and present validator.
|
||||
pub fn validate_consistency_with(&self, other: &Self) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_object_consistency(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl InterfaceDescriptor {
|
||||
/// Validates declaration structure, references, and absence of recursion.
|
||||
/// Validates declaration structure, named references, and absence of cycles.
|
||||
pub fn validate(&self) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_descriptor(self)
|
||||
}
|
||||
|
||||
/// Validates a value against a type expression in this descriptor.
|
||||
pub fn validate_value(&self, ty: &TypeExpr, value: &Value) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_root_value(self, ty, value)
|
||||
/// Validates a raw value against a type expression in this descriptor.
|
||||
pub fn validate_value(&self, r#type: &TypeExpr, value: &Value) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_root_value(self, r#type, value)
|
||||
}
|
||||
|
||||
/// Validates a call target and arguments against this descriptor.
|
||||
pub fn validate_call(&self, call: &OperationCall) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_call(self, call)
|
||||
}
|
||||
|
||||
/// Validates an operation's returned value against this descriptor.
|
||||
pub fn validate_return(
|
||||
/// Validates named arguments for one declared operation.
|
||||
pub fn validate_arguments(
|
||||
&self,
|
||||
operation: &str,
|
||||
value: Option<&Value>,
|
||||
arguments: &BTreeMap<String, Value>,
|
||||
) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_return(self, operation, value)
|
||||
crate::validation::validate_arguments(self, operation, arguments)
|
||||
}
|
||||
|
||||
/// Validates one operation result against its return declaration.
|
||||
pub fn validate_result(&self, operation: &str, result: &Value) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_result(self, operation, result)
|
||||
}
|
||||
|
||||
/// Validates the path, operation, and arguments in a call request.
|
||||
pub fn validate_call(&self, request: &CallOperationRequest) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_call(self, request)
|
||||
}
|
||||
}
|
||||
|
||||
impl FetchRequest {
|
||||
/// Validates the request path.
|
||||
pub fn validate(&self) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_path(&self.path)
|
||||
}
|
||||
|
||||
/// Validates an object returned for this request.
|
||||
pub fn validate_response(&self, response: &FetchResponse) -> Result<(), ValidationError> {
|
||||
response.validate_at_path(&self.path)
|
||||
}
|
||||
}
|
||||
|
||||
impl FetchTreeRequest {
|
||||
/// Validates the request path and nonzero node limit.
|
||||
pub fn validate(&self) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_fetch_tree_request(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl FetchTreeResponse {
|
||||
/// Validates tree shape, names, depth, and node limit for `request`.
|
||||
pub fn validate_for(&self, request: &FetchTreeRequest) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_fetch_tree_response(self, request)
|
||||
}
|
||||
}
|
||||
|
||||
impl FetchInterfaceResponse {
|
||||
/// Validates request correspondence and descriptor semantics.
|
||||
pub fn validate_for(&self, request: &FetchInterfaceRequest) -> Result<(), ValidationError> {
|
||||
crate::validation::validate_fetch_interface_response(self, request)
|
||||
}
|
||||
}
|
||||
|
||||
impl CallOperationRequest {
|
||||
/// Validates this call against the descriptor selected by `interface`.
|
||||
pub fn validate_with(&self, descriptor: &InterfaceDescriptor) -> Result<(), ValidationError> {
|
||||
descriptor.validate_call(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl CallOperationResponse {
|
||||
/// Validates this result against the named operation in `descriptor`.
|
||||
pub fn validate_for(
|
||||
&self,
|
||||
descriptor: &InterfaceDescriptor,
|
||||
operation: &str,
|
||||
) -> Result<(), ValidationError> {
|
||||
descriptor.validate_result(operation, &self.result)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user