Skip to main content

web_rpc/
describe.rs

1//! The compile-time description of a service trait.
2//!
3//! Every `#[web_rpc::service]` trait emits a `&'static Service` next to itself, named after
4//! the trait in `SCREAMING_SNAKE_CASE` with a `_DESCRIPTION` suffix (`FooBar` becomes
5//! `FOO_BAR_DESCRIPTION`). The description is role-agnostic: the same value describes the
6//! trait whether Javascript calls it or implements it. [`crate::js::endpoint`] renders
7//! Javascript and Typescript from it at compile time.
8//!
9//! Every type in a signature must implement either [`postcard_schema::Schema`] (the postcard
10//! route) or [`JsName`] (inside [`crate::wrap::Post`] or [`crate::wrap::Transfer`]).
11
12use postcard_schema::schema::NamedType;
13
14/// One `#[web_rpc::service]` trait.
15pub struct Service {
16    /// The trait's identifier, as written.
17    pub name: &'static str,
18    /// The trait's methods, one slice per method in declaration order.
19    ///
20    /// A method gated by a `#[cfg(...)]` that evaluates to false contributes an empty
21    /// slice. The method's index on the wire is its position among the non-empty entries,
22    /// which is also its variant index in the request enum.
23    pub methods: &'static [&'static [Method]],
24}
25
26/// One method of a service trait.
27pub struct Method {
28    /// The method's wire name, in camelCase.
29    pub name: &'static str,
30    /// The method's arguments, in declaration order.
31    pub args: &'static [Arg],
32    /// What the method sends back.
33    pub ret: Return,
34}
35
36/// One argument of a method.
37pub struct Arg {
38    /// The argument's name, in camelCase.
39    pub name: &'static str,
40    /// How the argument crosses the channel.
41    pub desc: &'static Desc,
42}
43
44/// How one value crosses the channel.
45///
46/// This mirrors [`crate::codec::WireArg`]: the `Option` and `Result` variants describe the
47/// wrapper structure that the macro walks, and the leaves say whether the value travels as a
48/// Javascript value or as postcard bytes.
49pub enum Desc {
50    /// A [`crate::wrap::Post`] or [`crate::wrap::Transfer`] leaf.
51    Js {
52        /// The DOM class name of the wrapped type, from [`JsName`].
53        name: &'static str,
54        /// Whether the value goes on the transfer list.
55        transfer: bool,
56    },
57    /// A postcard-encoded leaf.
58    Postcard(&'static NamedType),
59    /// A `&str` or `&[u8]` argument, written into the request payload directly with no
60    /// `WireArg` around it.
61    Inline(&'static NamedType),
62    /// `Option<T>`, whose `Some` and `None` route independently.
63    Option(&'static Desc),
64    /// `Result<T, E>`, whose `Ok` and `Err` route independently.
65    Result(&'static Desc, &'static Desc),
66}
67
68/// What a method sends back.
69pub enum Return {
70    /// No return type: a fire-and-forget notification, with no response message.
71    Notify,
72    /// A single response.
73    Value(&'static Desc),
74    /// A stream of items, each in its own message.
75    Stream(&'static Desc),
76}
77
78/// The DOM class name of a Javascript type, for the generated Typescript declarations.
79///
80/// Implemented for the `js_sys` types listed below and for the `web_sys` transferable objects.
81/// A Javascript type outside that list needs a local newtype implementing this trait, since
82/// the orphan rule prevents implementing it downstream for a foreign type.
83pub trait JsName {
84    /// The name this type has in Typescript.
85    const NAME: &'static str;
86}
87
88macro_rules! impl_js_name {
89    ($($ty:ty => $name:literal),* $(,)?) => {
90        $(impl JsName for $ty {
91            const NAME: &'static str = $name;
92        })*
93    };
94}
95
96impl_js_name! {
97    wasm_bindgen::JsValue => "unknown",
98    js_sys::Object => "object",
99    js_sys::Array => "unknown[]",
100    js_sys::Function => "Function",
101    js_sys::Promise => "Promise<unknown>",
102    js_sys::JsString => "string",
103    js_sys::Error => "Error",
104    js_sys::Date => "Date",
105    js_sys::RegExp => "RegExp",
106    js_sys::Map => "Map<unknown, unknown>",
107    js_sys::Set => "Set<unknown>",
108    js_sys::ArrayBuffer => "ArrayBuffer",
109    js_sys::SharedArrayBuffer => "SharedArrayBuffer",
110    js_sys::DataView => "DataView",
111    js_sys::Int8Array => "Int8Array",
112    js_sys::Uint8Array => "Uint8Array",
113    js_sys::Uint8ClampedArray => "Uint8ClampedArray",
114    js_sys::Int16Array => "Int16Array",
115    js_sys::Uint16Array => "Uint16Array",
116    js_sys::Int32Array => "Int32Array",
117    js_sys::Uint32Array => "Uint32Array",
118    js_sys::Float32Array => "Float32Array",
119    js_sys::Float64Array => "Float64Array",
120    js_sys::BigInt64Array => "BigInt64Array",
121    js_sys::BigUint64Array => "BigUint64Array",
122    web_sys::MessagePort => "MessagePort",
123    web_sys::OffscreenCanvas => "OffscreenCanvas",
124    web_sys::ImageBitmap => "ImageBitmap",
125    web_sys::ReadableStream => "ReadableStream",
126    web_sys::WritableStream => "WritableStream",
127    web_sys::TransformStream => "TransformStream",
128}