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