toolkit_zero/socket/mod.rs
1//! HTTP server and client utilities, selectively compiled via Cargo features.
2//!
3//! This module exposes two sub-modules behind feature flags:
4//!
5//! | Sub-module | Feature flag(s) |
6//! |---|---|
7//! | [`server`] | `socket` or `socket-server` |
8//! | [`client`] | `socket` or `socket-client` |
9//!
10//! Enable `socket` to get both, or opt into only one side with `socket-server` / `socket-client`.
11//!
12//! # Server
13//!
14//! Build typed HTTP routes using a fluent builder chain starting from [`server::ServerMechanism`].
15//! Routes are registered on a [`server::Server`] and served with a single `.await`.
16//!
17//! The chain supports:
18//! - **No extras** — handler receives no arguments
19//! - **JSON body** — via `.json::<T>()`, handler receives `T: DeserializeOwned`
20//! - **Query params** — via `.query::<T>()`, handler receives `T: DeserializeOwned`
21//! - **Shared state** — via `.state(s)`, handler receives a clone of `S`
22//! - **State + JSON / State + query** — combinations of the above
23//!
24//! Each branch finalises with `.onconnect(async handler)` or the unsafe `.onconnect_sync(sync handler)`.
25//! Use the [`reply!`] macro (or standalone helpers) to construct the response.
26//!
27//! ```rust,no_run
28//! # use toolkit_zero::socket::server::*;
29//! # use serde::{Deserialize, Serialize};
30//! # #[derive(Deserialize, Serialize)] struct Item { id: u32, name: String }
31//! # #[derive(Deserialize)] struct NewItem { name: String }
32//! # #[derive(Deserialize)] struct Filter { page: u32 }
33//! # use std::sync::{Arc, Mutex};
34//!
35//! let store: Arc<Mutex<Vec<Item>>> = Arc::new(Mutex::new(vec![]));
36//!
37//! let mut server = Server::default();
38//! server
39//! .mechanism(
40//! ServerMechanism::get("/health")
41//! .onconnect(|| async { reply!() })
42//! )
43//! .mechanism(
44//! ServerMechanism::post("/items")
45//! .json::<NewItem>()
46//! .onconnect(|body| async move {
47//! let item = Item { id: 1, name: body.name };
48//! reply!(json => item, status => Status::Created)
49//! })
50//! )
51//! .mechanism(
52//! ServerMechanism::get("/items")
53//! .state(store.clone())
54//! .onconnect(|state| async move {
55//! let items = state.lock().unwrap().clone();
56//! reply!(json => items)
57//! })
58//! )
59//! .mechanism(
60//! ServerMechanism::get("/items/search")
61//! .query::<Filter>()
62//! .onconnect(|filter| async move {
63//! let _ = filter.page;
64//! reply!()
65//! })
66//! );
67//!
68//! // server.serve(([0, 0, 0, 0], 8080)).await;
69//! ```
70//!
71//! # Client
72//!
73//! Make typed HTTP requests using a fluent builder chain starting from [`client::Client`].
74//! The full URL is constructed automatically from a [`client::Target`] (localhost port or remote URL)
75//! and the endpoint string. Both async (`.send()`) and sync (`.send_sync()`) are supported.
76//!
77//! The chain supports:
78//! - **Plain** — no body, no query
79//! - **JSON body** — via `.json(value)`, serialises with `serde`
80//! - **Query params** — via `.query(value)`, serialised into the URL query string
81//!
82//! All seven HTTP methods are available: `get`, `post`, `put`, `delete`, `patch`, `head`, `options`.
83//!
84//! ```rust,no_run
85//! # use toolkit_zero::socket::client::*;
86//! # use toolkit_zero::socket::server::*;
87//! # use serde::{Deserialize, Serialize};
88//! # #[derive(Deserialize, Serialize, Clone)] struct Item { id: u32, name: String }
89//! # #[derive(Deserialize, Serialize)] struct NewItem { name: String }
90//! # #[derive(Deserialize, Serialize)] struct Filter { page: u32 }
91//! # async fn example() -> Result<(), reqwest::Error> {
92//!
93//! let client = Client::new(Target::Localhost(8080));
94//!
95//! // ── GET /items ───────────────────────────────────────────────────────────
96//! // Server:
97//! // ServerMechanism::get("/items")
98//! // .onconnect(|| async {
99//! // let items: Vec<Item> = vec![];
100//! // reply!(json => items)
101//! // })
102//! let items: Vec<Item> = client.get("/items").send().await?;
103//!
104//! // ── POST /items (JSON body) ──────────────────────────────────────────────
105//! // Server:
106//! // ServerMechanism::post("/items")
107//! // .json::<NewItem>()
108//! // .onconnect(|body| async move {
109//! // let item = Item { id: 1, name: body.name };
110//! // reply!(json => item, status => Status::Created)
111//! // })
112//! let created: Item = client
113//! .post("/items")
114//! .json(NewItem { name: "widget".to_string() })
115//! .send()
116//! .await?;
117//!
118//! // ── GET /items (query params) ────────────────────────────────────────────
119//! // Server:
120//! // ServerMechanism::get("/items")
121//! // .query::<Filter>()
122//! // .onconnect(|filter| async move {
123//! // let _ = filter.page;
124//! // reply!(json => Vec::<Item>::new())
125//! // })
126//! let page: Vec<Item> = client
127//! .get("/items")
128//! .query(Filter { page: 2 })
129//! .send()
130//! .await?;
131//!
132//! // ── DELETE /items/1 (sync) ───────────────────────────────────────────────
133//! // Server:
134//! // ServerMechanism::delete("/items/1")
135//! // .onconnect(|| async {
136//! // reply!(message => warp::reply(), status => Status::NoContent)
137//! // })
138//! let _: Item = client.delete("/items/1").send_sync()?;
139//!
140//! # Ok(())
141//! # }
142//! ```
143
144// ─── Serialization key (shared by server and client) ────────────────────────
145
146/// Controls which VEIL key is used when the body or query parameters are sealed.
147///
148/// Pass this to [`server::ServerMechanism::encryption`], [`server::ServerMechanism::encrypted_query`],
149/// [`client::RequestBuilder::encryption`], and [`client::RequestBuilder::encrypted_query`].
150/// For plain-JSON routes use the existing `.json()` / `.query()` builder methods instead.
151///
152/// | Variant | Wire format | Trait requirements on `T` |
153/// |---|---|---|
154/// | `Default` | VEIL-sealed bytes (`application/octet-stream`) | `bincode::Encode` / `Decode<()>` |
155/// | `Value(key)` | VEIL-sealed bytes with a custom key | `bincode::Encode` / `Decode<()>` |
156#[derive(Clone)]
157pub enum SerializationKey {
158 /// Use the built-in default VEIL key (`"serialization/deserialization"`).
159 Default,
160 /// Use a custom VEIL key shared by both client and server.
161 Value(String),
162}
163
164impl SerializationKey {
165 #[doc(hidden)]
166 pub fn veil_key(&self) -> Option<&str> {
167 match self {
168 Self::Default => None,
169 Self::Value(k) => Some(k.as_str()),
170 }
171 }
172}
173
174#[cfg(feature = "socket-server")]
175pub mod server;
176
177#[cfg(feature = "socket-client")]
178pub mod client;
179
180#[cfg(any(feature = "socket-server", feature = "socket-client"))]
181pub mod prelude;
182
183#[cfg(feature = "backend-deps")]
184pub mod backend_deps;