100 lines
3.7 KiB
Markdown
100 lines
3.7 KiB
Markdown
# wip-rs
|
|
|
|
Rust building blocks for the Web Interface Protocol (WIP).
|
|
|
|
## Interface authoring
|
|
|
|
Applications depend on `wip-interface`, normally imported as `wip`:
|
|
|
|
```rust
|
|
use wip_interface as wip;
|
|
|
|
#[derive(wip::WipType)]
|
|
struct Message {
|
|
/// Message body.
|
|
text: String,
|
|
}
|
|
|
|
#[wip::interface]
|
|
pub trait Messenger {
|
|
/// Sends a message.
|
|
///
|
|
/// # Returns
|
|
/// Whether the application accepted the message.
|
|
fn send(
|
|
&self,
|
|
/// Message to send.
|
|
message: Message,
|
|
) -> wip::OperationResult<bool>;
|
|
}
|
|
```
|
|
|
|
The `interface` attribute and `WipType` derive are re-exported by the facade;
|
|
users do not import `wip-interface-macros` directly. The generated
|
|
`MessengerDefinition` constructs an `InterfaceDescriptor`, decodes named
|
|
arguments, encodes results, validates both boundaries, and provides typed
|
|
dispatch for any `T: Messenger`.
|
|
|
|
### Type mapping
|
|
|
|
| Rust | Protocol |
|
|
| --- | --- |
|
|
| `()` as an operation result | no returned value |
|
|
| `bool` | boolean |
|
|
| integer primitives | JavaScript-lossless integer |
|
|
| `f32`, `f64` | finite number |
|
|
| `String` | string |
|
|
| `wip::Bytes` | bytes |
|
|
| `wip::wip_protocol::JsonValue` | JSON |
|
|
| `wip::Entry<K, V>` | entry |
|
|
| `Vec<T>` | list |
|
|
| named-field `#[derive(wip::WipType)]` struct | named record |
|
|
| all-unit `#[derive(wip::WipType)]` enum | named enum |
|
|
| all-newtype `#[derive(wip::WipType)]` enum | named tagged union |
|
|
|
|
`Option<T>` is accepted only for a direct DTO field or operation parameter and
|
|
means that the named value may be omitted. It is not protocol null. Domain
|
|
failures are modeled in the application result (usually a derived union) and
|
|
returned through `Ok`; `wip::HostError` is only for host/runtime failures.
|
|
|
|
Rust doc comments become descriptor metadata. For an operation, a `# Returns`
|
|
section documents the return declaration; text before that section documents
|
|
the operation itself.
|
|
|
|
The supported interface slice is deliberately non-generic: public traits,
|
|
`&self`, named parameters, and `OperationResult<T>`. Generic traits or methods,
|
|
associated items, custom lifetimes, inheritance, default method bodies, and
|
|
other receiver forms are rejected at their source spans.
|
|
|
|
## Host publication and dispatch
|
|
|
|
Applications use `wip-host` to register generated definitions and explicitly
|
|
choose the ordered interface set for each object publication:
|
|
|
|
```rust,ignore
|
|
let mut interfaces = wip_host::InterfaceRegistry::new();
|
|
let messenger = interfaces.register(MessengerDefinition, None)?;
|
|
|
|
let published = wip_host::PublishedObject::new(request_scoped_object)
|
|
.expose(&messenger)?;
|
|
let protocol_object = published.protocol_object();
|
|
```
|
|
|
|
`expose` compiles only when the object implements the generated definition's
|
|
interface trait. Implementing a trait does not expose it automatically, empty
|
|
interface sets are valid, insertion order is preserved, and duplicate
|
|
references are rejected. Registry references are opaque; descriptor names and
|
|
Rust type names are not used as wire identity.
|
|
|
|
Dispatch is qualified by interface reference and operation name. The host checks
|
|
current object identity and validator policy, current interface membership,
|
|
interface validator, descriptor operation, arguments, and result while retaining
|
|
the selected object and registered artifact. Host-private fields and backend
|
|
handles are never projected by `WipObject`.
|
|
|
|
Providers remain responsible for backend atomicity. Enter a transaction, lock,
|
|
stable-handle scope, or conditional-update primitive before constructing and
|
|
dispatching the request-scoped `PublishedObject`, and retain it until dispatch
|
|
returns. The runtime never validates one object and then resolves a second
|
|
snapshot, but it cannot make an arbitrary external backend atomic by itself.
|