wasm_runner_sdk/
lib.rs

1//! # wasm-runner-sdk
2//!
3//! High-level SDK for building WASM modules for wasm-runner.
4//!
5//! Note: This crate is highly experimental and the APIs may change without notice.
6//!
7//! This SDK provides an axum-like API for handling HTTP requests in WASM,
8//! with automatic extraction of request data and ergonomic response building.
9//!
10//! ## Quick Start
11//!
12//! ```ignore
13//! use wasm_runner_sdk::prelude::*;
14//!
15//! #[derive(Deserialize)]
16//! struct HelloQuery {
17//!     name: Option<String>,
18//! }
19//!
20//! fn handler(Query(params): Query<HelloQuery>) -> impl IntoResponse {
21//!     let name = params.name.as_deref().unwrap_or("World");
22//!     Json(serde_json::json!({
23//!         "message": format!("Hello, {}!", name)
24//!     }))
25//! }
26//!
27//! #[no_mangle]
28//! pub extern "C" fn _start() {
29//!     run(handler);
30//! }
31//! ```
32//!
33//! ## Extractors
34//!
35//! Extractors allow you to extract data from requests in a type-safe way:
36//!
37//! - `Query<T>` - Extract query parameters as a struct
38//! - `Path<T>` - Extract path segments
39//! - `Json<T>` - Extract JSON body
40//! - `Body` - Extract raw body bytes
41//! - `Headers` - Extract all headers
42//! - `Cookies` - Extract cookies
43//! - `BearerToken` - Extract Bearer token from Authorization header
44//!
45//! ## Responses
46//!
47//! Any type implementing `IntoResponse` can be returned from a handler:
48//!
49//! - `String` / `&str` - Plain text response
50//! - `Json<T>` - JSON response
51//! - `Html<T>` - HTML response
52//! - `Response` - Full control over the response
53//! - `StatusCode` - Empty response with status
54//! - `(StatusCode, T)` - Response with custom status
55//! - `Result<T, E>` - Ok returns T, Err returns E
56//! - `Option<T>` - Some returns T, None returns 404
57//!
58//! ## HTTP Client
59//!
60//! Make outbound HTTP requests using the `http` module:
61//!
62//! ```ignore
63//! use wasm_runner_sdk::http::Client;
64//!
65//! let client = Client::new();
66//! let response = client.get("https://api.example.com/data").send()?;
67//! let data: MyData = response.json()?;
68//! ```
69//!
70//! ## Capabilities
71//!
72//! Query the enabled capabilities at runtime:
73//!
74//! ```ignore
75//! use wasm_runner_sdk::prelude::*;
76//!
77//! if capabilities::has(Capability::Network) {
78//!     let client = Client::new();
79//!     let _ = client.get("https://example.com").send()?;
80//! }
81//! ```
82
83#![allow(clippy::module_name_repetitions)]
84
85pub mod abi;
86pub mod capabilities;
87pub mod extract;
88pub mod handler;
89pub mod http;
90pub mod log;
91pub mod request;
92pub mod response;
93pub mod router;
94
95// Re-export commonly used types at the crate root
96pub use extract::{
97    Authorization, BearerToken, Body, BodyString, ContentType, Cookies, ExtractError, FromRequest,
98    FullPath, Headers, Json as ExtractJson, MethodExtractor, Path, PathSegments, Query, QueryMap,
99};
100pub use handler::Handler;
101pub use request::{Method, Request};
102pub use response::{Html, IntoResponse, Json, Redirect, Response, StatusCode};
103pub use router::{Param, Params, Router};
104pub use capabilities as runtime_capabilities;
105pub use capabilities::Capability;
106
107// Re-export serde for user convenience
108pub use serde;
109pub use serde_json;
110
111/// Prelude module for convenient imports.
112///
113/// Import everything you need with:
114/// ```ignore
115/// use wasm_runner_sdk::prelude::*;
116/// ```
117pub mod prelude {
118    pub use crate::extract::{
119        Authorization, BearerToken, Body, BodyString, ContentType, Cookies, ExtractError,
120        FromRequest, FullPath, Headers, Json as ExtractJson, MethodExtractor, Path, PathSegments,
121        Query, QueryMap,
122    };
123    pub use crate::handler::Handler;
124    pub use crate::http::{Client, HttpError, HttpResponse};
125    pub use crate::log::{error, info, warn, Level};
126    pub use crate::request::{Method, Request};
127    pub use crate::response::{Html, IntoResponse, Json, Redirect, Response, StatusCode};
128    pub use crate::router::{Param, Params, Router};
129    pub use crate::runtime_capabilities as capabilities;
130    pub use crate::Capability;
131    pub use crate::{log_error, log_info, log_warn, run, run_with_request};
132    pub use serde::{Deserialize, Serialize};
133    pub use serde_json;
134}
135
136/// Runs a handler function, extracting request data and sending the response.
137///
138/// This is the main entry point for handling requests. It:
139/// 1. Creates a new Request from the host
140/// 2. Calls the handler function with extracted data
141/// 3. Converts the result to a Response
142/// 4. Sends the response to the host
143///
144/// # Example
145///
146/// ```ignore
147/// use wasm_runner_sdk::prelude::*;
148///
149/// fn my_handler() -> impl IntoResponse {
150///     "Hello, World!"
151/// }
152///
153/// #[no_mangle]
154/// pub extern "C" fn _start() {
155///     run(my_handler);
156/// }
157/// ```
158pub fn run<H, Args>(handler: H)
159where
160    H: Handler<Args>,
161    H::Output: IntoResponse,
162{
163    let mut request = Request::new();
164    let method = request.method();
165    let path = request.path().to_string();
166    log_info!("Handling request {} {}", method, path);
167    let response = handler.call(&mut request);
168    response.into_response().send();
169}
170
171/// Runs a handler function with direct access to the request.
172///
173/// Use this when you need mutable access to the request within your handler.
174///
175/// # Example
176///
177/// ```ignore
178/// use wasm_runner_sdk::prelude::*;
179///
180/// fn my_handler(req: &mut Request) -> impl IntoResponse {
181///     let method = req.method();
182///     let path = req.path();
183///     format!("{} {}", method, path)
184/// }
185///
186/// #[no_mangle]
187/// pub extern "C" fn _start() {
188///     run_with_request(my_handler);
189/// }
190/// ```
191pub fn run_with_request<F, R>(handler: F)
192where
193    F: FnOnce(&mut Request) -> R,
194    R: IntoResponse,
195{
196    let mut request = Request::new();
197    let method = request.method();
198    let path = request.path().to_string();
199    log_info!("Handling request {} {}", method, path);
200    let response = handler(&mut request);
201    response.into_response().send();
202}