toolkit_zero/lib.rs
1//! # toolkit-zero
2//!
3//! A feature-selective Rust utility toolkit. Pull in only what you need via Cargo
4//! feature flags — each feature compiles exactly the modules it requires and nothing
5//! more.
6//!
7//! ---
8//!
9//! ## Table of Contents
10//!
11//! 1. [Feature flags](#feature-flags)
12//! 2. [Serialization](#serialization)
13//! 3. [Socket — server](#socket--server)
14//! 4. [Socket — client](#socket--client)
15//! 5. [Location](#location)
16//!
17//! ---
18//!
19//! ## Feature flags
20//!
21//! | Feature | Enables | Exposes |
22//! |---|---|---|
23//! | `serialization` | VEIL cipher (seal / open) | [`serialization`] |
24//! | `socket-server` | VEIL + typed HTTP server builder | [`socket::server`] |
25//! | `socket-client` | VEIL + typed HTTP client builder | [`socket::client`] |
26//! | `socket` | Both `socket-server` and `socket-client` | both |
27//! | `location-native` | Browser-based geolocation | [`location::browser`] |
28//! | `location` | Alias for `location-native` | [`location`] |
29//!
30//! ```toml
31//! [dependencies]
32//! # Only the VEIL cipher
33//! toolkit-zero = { version = "2", features = ["serialization"] }
34//!
35//! # HTTP server only
36//! toolkit-zero = { version = "2", features = ["socket-server"] }
37//!
38//! # HTTP client only
39//! toolkit-zero = { version = "2", features = ["socket-client"] }
40//!
41//! # Both sides of the socket
42//! toolkit-zero = { version = "2", features = ["socket"] }
43//!
44//! # Geolocation (bundles socket-server automatically)
45//! toolkit-zero = { version = "2", features = ["location"] }
46//! ```
47//!
48//! ---
49//!
50//! ## Serialization
51//!
52//! The `serialization` feature exposes the **VEIL cipher** — a custom,
53//! key-dependent binary codec that converts any [`bincode`]-encodable value into
54//! an opaque byte sequence and back.
55//!
56//! The two entry points are [`serialization::seal`] and [`serialization::open`].
57//! Every output byte depends on the full message and the key; without the exact
58//! key, the output cannot be inverted.
59//!
60//! ```rust,no_run
61//! use toolkit_zero::serialization::{seal, open, Encode, Decode};
62//!
63//! #[derive(Encode, Decode, Debug, PartialEq)]
64//! struct Point { x: f64, y: f64 }
65//!
66//! let p = Point { x: 1.0, y: -2.0 };
67//! let blob = seal(&p, None).unwrap(); // default key
68//! let back: Point = open(&blob, None).unwrap();
69//! assert_eq!(p, back);
70//!
71//! let blob2 = seal(&p, Some("my-key")).unwrap(); // custom key
72//! let back2: Point = open(&blob2, Some("my-key")).unwrap();
73//! assert_eq!(p, back2);
74//! ```
75//!
76//! ---
77//!
78//! ## Socket — server
79//!
80//! The `socket-server` feature exposes a fluent builder API for declaring typed
81//! HTTP routes and serving them. Start every route with [`socket::server::ServerMechanism`],
82//! optionally add a JSON body expectation, URL query parameters, or shared state,
83//! then finalise with `.onconnect(async_handler)`. Register all routes on a
84//! [`socket::server::Server`] and call `.serve(addr).await`.
85//!
86//! The [`socket::server::reply!`] macro is the primary way to construct responses.
87//!
88//! ```rust,no_run
89//! use toolkit_zero::socket::server::{Server, ServerMechanism, reply, Status};
90//! use serde::{Deserialize, Serialize};
91//! use std::sync::{Arc, Mutex};
92//!
93//! #[derive(Deserialize, Serialize, Clone)]
94//! struct Item { id: u32, name: String }
95//!
96//! #[derive(Deserialize)]
97//! struct NewItem { name: String }
98//!
99//! #[derive(Deserialize)]
100//! struct Filter { page: u32 }
101//!
102//! # async fn run() {
103//! let store: Arc<Mutex<Vec<Item>>> = Arc::new(Mutex::new(vec![]));
104//!
105//! let mut server = Server::default();
106//! server
107//! // Plain GET — no body, no state
108//! .mechanism(
109//! ServerMechanism::get("/health")
110//! .onconnect(|| async { reply!() })
111//! )
112//! // POST with a JSON body
113//! .mechanism(
114//! ServerMechanism::post("/items")
115//! .json::<NewItem>()
116//! .onconnect(|body: NewItem| async move {
117//! reply!(json => Item { id: 1, name: body.name }, status => Status::Created)
118//! })
119//! )
120//! // GET with shared state
121//! .mechanism(
122//! ServerMechanism::get("/items")
123//! .state(store.clone())
124//! .onconnect(|state: Arc<Mutex<Vec<Item>>>| async move {
125//! let items = state.lock().unwrap().clone();
126//! reply!(json => items)
127//! })
128//! )
129//! // GET with URL query parameters
130//! .mechanism(
131//! ServerMechanism::get("/items/search")
132//! .query::<Filter>()
133//! .onconnect(|f: Filter| async move {
134//! let _ = f.page;
135//! reply!()
136//! })
137//! );
138//!
139//! server.serve(([127, 0, 0, 1], 8080)).await;
140//! # }
141//! ```
142//!
143//! VEIL-encrypted routes are also supported via
144//! [`socket::server::ServerMechanism::encryption`] and
145//! [`socket::server::ServerMechanism::encrypted_query`].
146//! The body or query is decrypted before the handler is called; a wrong key or
147//! corrupt payload returns `403 Forbidden` automatically.
148//!
149//! ---
150//!
151//! ## Socket — client
152//!
153//! The `socket-client` feature exposes a fluent [`socket::client::Client`] for
154//! issuing typed HTTP requests. Construct a client from a
155//! [`socket::client::Target`] (a `localhost` port or a remote URL), pick an HTTP
156//! method, optionally attach a body or query, and call `.send().await` (async) or
157//! `.send_sync()` (blocking).
158//!
159//! ```rust,no_run
160//! use toolkit_zero::socket::client::{Client, Target};
161//! use serde::{Deserialize, Serialize};
162//!
163//! #[derive(Deserialize, Serialize, Clone)]
164//! struct Item { id: u32, name: String }
165//!
166//! #[derive(Serialize)]
167//! struct NewItem { name: String }
168//!
169//! #[derive(Serialize)]
170//! struct Filter { page: u32 }
171//!
172//! # async fn run() -> Result<(), reqwest::Error> {
173//! // Async-only client — safe inside #[tokio::main]
174//! let client = Client::new_async(Target::Localhost(8080));
175//!
176//! // Plain GET
177//! let items: Vec<Item> = client.get("/items").send().await?;
178//!
179//! // POST with JSON body
180//! let created: Item = client
181//! .post("/items")
182//! .json(NewItem { name: "widget".into() })
183//! .send()
184//! .await?;
185//!
186//! // GET with query params
187//! let page: Vec<Item> = client
188//! .get("/items")
189//! .query(Filter { page: 2 })
190//! .send()
191//! .await?;
192//!
193//! // Synchronous DELETE (Client::new_sync must be called outside any async runtime)
194//! let _: Item = client.delete("/items/1").send_sync()?;
195//! # Ok(())
196//! # }
197//! ```
198//!
199//! VEIL-encrypted requests are available via
200//! [`socket::client::RequestBuilder::encryption`] and
201//! [`socket::client::RequestBuilder::encrypted_query`].
202//! The body or query parameters are sealed before the wire send; the response is
203//! opened automatically.
204//!
205//! ---
206//!
207//! ## Location
208//!
209//! The `location` (or `location-native`) feature exposes browser-based geographic
210//! coordinate acquisition. A temporary local HTTP server is bound on a random
211//! port, the system's default browser is opened to a consent page, and the
212//! standard browser Geolocation API POSTs the coordinates back. The server shuts
213//! itself down once a result arrives.
214//!
215//! Two entry points are available in [`location::browser`]:
216//!
217//! | Function | Context |
218//! |---|---|
219//! | [`location::browser::__location__`] | Blocking — safe from sync or async |
220//! | [`location::browser::__location_async__`] | Async — preferred inside `#[tokio::main]` |
221//!
222//! ```rust,no_run
223//! use toolkit_zero::location::browser::{__location__, __location_async__, PageTemplate};
224//!
225//! // Blocking — works from sync main or from inside a Tokio runtime
226//! match __location__(PageTemplate::default()) {
227//! Ok(data) => println!("lat={:.6} lon={:.6} ±{:.0}m",
228//! data.latitude, data.longitude, data.accuracy),
229//! Err(e) => eprintln!("location error: {e}"),
230//! }
231//!
232//! // Async — preferred when already inside #[tokio::main]
233//! # async fn run() {
234//! match __location_async__(PageTemplate::default()).await {
235//! Ok(data) => println!("lat={:.6} lon={:.6}", data.latitude, data.longitude),
236//! Err(e) => eprintln!("location error: {e}"),
237//! }
238//! # }
239//! ```
240//!
241//! The [`location::browser::PageTemplate`] enum controls what the user sees:
242//! a plain single-button page, a checkbox-gated variant, or a fully custom HTML
243//! document.
244
245#[cfg(any(feature = "socket", feature = "socket-server", feature = "socket-client"))]
246pub mod socket;
247
248#[cfg(any(feature = "location", feature = "location-native"))]
249pub mod location;
250
251#[cfg(feature = "serialization")]
252pub mod serialization;