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}