# 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; } ``` 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` | entry | | `Vec` | 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` 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`. 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.