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}