Skip to main content

toolkit_zero/
lib.rs

1//! # toolkit-zero
2//!
3//! A feature-selective Rust utility crate. Declare only the functionality your
4//! project requires via Cargo feature flags; each feature compiles exclusively
5//! the code it depends on, with no extraneous overhead.
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//! 6. [Encryption — Timelock](#encryption--timelock)
17//! 7. [Dependency Graph — BuildTimeFingerprint](#dependency-graph--buildtimefingerprint)
18//! 8. [Backend deps](#backend-deps-1)
19//!
20//! ---
21//!
22//! ## Feature flags
23//!
24//! | Feature | Enables | Exposes |
25//! |---|---|---|
26//! | `serialization` | VEIL cipher (seal / open) | [`serialization`] |
27//! | `socket-server` | VEIL + typed HTTP server builder | [`socket::server`] |
28//! | `socket-client` | VEIL + typed HTTP client builder | [`socket::client`] |
29//! | `socket` | Both `socket-server` and `socket-client` | both |
30//! | `location-browser` | Browser-based geolocation | [`location::browser`] |
31//! | `location` | Alias for `location-browser` | [`location`] |
32//! | `enc-timelock-keygen-now` | Time-lock key derivation from the system clock | [`encryption::timelock::derive_key_now`] |
33//! | `enc-timelock-keygen-input` | Time-lock key derivation from a caller-supplied time | [`encryption::timelock::derive_key_at`] |
34//! | `enc-timelock-async-keygen-now` | Async variant of `enc-timelock-keygen-now` | [`encryption::timelock::derive_key_now_async`] |
35//! | `enc-timelock-async-keygen-input` | Async variant of `enc-timelock-keygen-input` | [`encryption::timelock::derive_key_at_async`] |
36//! | `encryption` | All four `enc-timelock-*` features | [`encryption::timelock`] |
37//! | `dependency-graph-build` | Attach a normalised dependency-graph snapshot (`fingerprint.json`) at build time | [`dependency_graph::build`] |
38//! | `dependency-graph-capture` | Read the embedded `fingerprint.json` snapshot at runtime | [`dependency_graph::capture`] |
39//! | `backend-deps` | Re-exports all third-party deps used by each active module | `*::backend_deps` |
40//!
41//! ```toml
42//! [dependencies]
43//! # Only the VEIL cipher
44//! toolkit-zero = { version = "3", features = ["serialization"] }
45//!
46//! # HTTP server only
47//! toolkit-zero = { version = "3", features = ["socket-server"] }
48//!
49//! # HTTP client only
50//! toolkit-zero = { version = "3", features = ["socket-client"] }
51//!
52//! # Both sides of the socket
53//! toolkit-zero = { version = "3", features = ["socket"] }
54//!
55//! # Geolocation (bundles socket-server automatically)
56//! toolkit-zero = { version = "3", features = ["location"] }
57//!
58//! # Full time-lock encryption suite
59//! toolkit-zero = { version = "3", features = ["encryption"] }
60//!
61//! # Attach build-time fingerprint in build.rs
62//! # [build-dependencies]
63//! toolkit-zero = { version = "3", features = ["dependency-graph-build"] }
64//!
65//! # Read build-time fingerprint at runtime
66//! # [dependencies]
67//! toolkit-zero = { version = "3", features = ["dependency-graph-capture"] }
68//!
69//! # Re-export deps alongside socket-server
70//! toolkit-zero = { version = "3", features = ["socket-server", "backend-deps"] }
71//! ```
72//!
73//! ---
74//!
75//! ## Serialization
76//!
77//! The `serialization` feature exposes the **VEIL cipher** — a custom,
78//! key-dependent binary codec that transforms any [`bincode`]-encodable value
79//! into an opaque byte sequence and back.
80//!
81//! Keys are moved into [`serialization::seal`] / [`serialization::open`] and
82//! wrapped in `Zeroizing<String>` internally, wiping them from memory on drop.
83//!
84//! The two entry points are [`serialization::seal`] and [`serialization::open`].
85//! Every output byte is a function of the complete input and the key; without
86//! the exact key, the output cannot be reversed.
87//!
88//! Three attribute macros are also available via the `serialization` feature:
89//!
90//! | Macro | Purpose |
91//! |---|---|
92//! | [`serialization::serializable`] | derive `Encode+Decode` + inject `seal`/`open` methods |
93//! | [`serialization::serialize`] | inline seal to a variable or a file |
94//! | [`serialization::deserialize`] | inline open from a variable blob or a file |
95//!
96//! ```rust,ignore
97//! use toolkit_zero::serialization::{seal, open, Encode, Decode};
98//!
99//! #[derive(Encode, Decode, Debug, PartialEq)]
100//! struct Point { x: f64, y: f64 }
101//!
102//! let p = Point { x: 1.0, y: -2.0 };
103//! let blob = seal(&p, None).unwrap();                          // default key
104//! let back: Point = open(&blob, None).unwrap();
105//! assert_eq!(p, back);
106//!
107//! let blob2 = seal(&p, Some("my-key".to_string())).unwrap();              // custom key
108//! let back2: Point = open(&blob2, Some("my-key".to_string())).unwrap();
109//! assert_eq!(p, back2);
110//! ```
111//!
112//! ---
113//!
114//! ## Socket — server
115//!
116//! The `socket-server` feature exposes a fluent, type-safe builder API for
117//! declaring and serving HTTP routes. Begin each route with
118//! [`socket::server::ServerMechanism`], optionally attach a JSON body expectation,
119//! URL query parameters, or shared state, then finalise with `.onconnect(handler)`.
120//! Register routes on a [`socket::server::Server`] and serve them with a single
121//! call to `.serve(addr).await`.
122//!
123//! The [`socket::server::reply!`] macro is the primary way to construct responses.
124//!
125//! ```rust,ignore
126//! use toolkit_zero::socket::server::{Server, ServerMechanism, reply, Status};
127//! use serde::{Deserialize, Serialize};
128//! use std::sync::{Arc, Mutex};
129//!
130//! #[derive(Deserialize, Serialize, Clone)]
131//! struct Item { id: u32, name: String }
132//!
133//! #[derive(Deserialize)]
134//! struct NewItem { name: String }
135//!
136//! #[derive(Deserialize)]
137//! struct Filter { page: u32 }
138//!
139//! # async fn run() {
140//! let store: Arc<Mutex<Vec<Item>>> = Arc::new(Mutex::new(vec![]));
141//!
142//! let mut server = Server::default();
143//! server
144//!     // Plain GET — no body, no state
145//!     .mechanism(
146//!         ServerMechanism::get("/health")
147//!             .onconnect(|| async { reply!() })
148//!     )
149//!     // POST with a JSON body
150//!     .mechanism(
151//!         ServerMechanism::post("/items")
152//!             .json::<NewItem>()
153//!             .onconnect(|body: NewItem| async move {
154//!                 reply!(json => Item { id: 1, name: body.name }, status => Status::Created)
155//!             })
156//!     )
157//!     // GET with shared state
158//!     .mechanism(
159//!         ServerMechanism::get("/items")
160//!             .state(store.clone())
161//!             .onconnect(|state: Arc<Mutex<Vec<Item>>>| async move {
162//!                 let items = state.lock().unwrap().clone();
163//!                 reply!(json => items)
164//!             })
165//!     )
166//!     // GET with URL query parameters
167//!     .mechanism(
168//!         ServerMechanism::get("/items/search")
169//!             .query::<Filter>()
170//!             .onconnect(|f: Filter| async move {
171//!                 let _ = f.page;
172//!                 reply!()
173//!             })
174//!     );
175//!
176//! server.serve(([127, 0, 0, 1], 8080)).await;
177//! # }
178//! ```
179//!
180//! VEIL-encrypted routes are also supported via
181//! [`socket::server::ServerMechanism::encryption`] and
182//! [`socket::server::ServerMechanism::encrypted_query`].
183//! The body or query is decrypted before the handler is called; a wrong key or
184//! corrupt payload returns `403 Forbidden` automatically.
185//!
186//! ### `#[mechanism]` attribute macro
187//!
188//! The [`socket::server::mechanism`] attribute macro is a concise alternative to
189//! the builder calls above. It replaces the decorated `async fn` in-place with the
190//! equivalent `server.mechanism(...)` statement — no separate registration step
191//! required. All 10 builder combinations are supported: plain, `json`, `query`,
192//! `state`, `encrypted`, `encrypted_query`, and every `state + …` combination.
193//!
194//! ```rust,ignore
195//! use toolkit_zero::socket::server::{Server, mechanism, reply, Status};
196//! use serde::{Deserialize, Serialize};
197//!
198//! #[derive(Deserialize)]
199//! struct NewItem { name: String }
200//!
201//! #[derive(Serialize, Clone)]
202//! struct Item { id: u32, name: String }
203//!
204//! # async fn run() {
205//! let mut server = Server::default();
206//!
207//! #[mechanism(server, GET, "/health")]
208//! async fn health() { reply!() }
209//!
210//! #[mechanism(server, POST, "/items", json)]
211//! async fn create_item(body: NewItem) {
212//!     reply!(json => Item { id: 1, name: body.name }, status => Status::Created)
213//! }
214//!
215//! server.serve(([127, 0, 0, 1], 8080)).await;
216//! # }
217//! ```
218//!
219//! See [`socket::server::mechanism`] for the full syntax reference.
220//!
221//! ---
222//!
223//! ## Socket — client
224//!
225//! The `socket-client` feature exposes a fluent [`socket::client::Client`] for
226//! issuing HTTP requests. Construct a client from a
227//! [`socket::client::Target`] (a localhost port or a remote URL), select an HTTP
228//! method, optionally attach a body or query parameters, and call `.send().await`
229//! (async) or `.send_sync()` (blocking).
230//!
231//! ```rust,ignore
232//! use toolkit_zero::socket::client::{Client, Target};
233//! use serde::{Deserialize, Serialize};
234//!
235//! #[derive(Deserialize, Serialize, Clone)]
236//! struct Item { id: u32, name: String }
237//!
238//! #[derive(Serialize)]
239//! struct NewItem { name: String }
240//!
241//! #[derive(Serialize)]
242//! struct Filter { page: u32 }
243//!
244//! # async fn run() -> Result<(), reqwest::Error> {
245//! // Async-only client — safe inside #[tokio::main]
246//! let client = Client::new_async(Target::Localhost(8080));
247//!
248//! // Plain GET
249//! let items: Vec<Item> = client.get("/items").send().await?;
250//!
251//! // POST with JSON body
252//! let created: Item = client
253//!     .post("/items")
254//!     .json(NewItem { name: "widget".into() })
255//!     .send()
256//!     .await?;
257//!
258//! // GET with query params
259//! let page: Vec<Item> = client
260//!     .get("/items")
261//!     .query(Filter { page: 2 })
262//!     .send()
263//!     .await?;
264//!
265//! // Synchronous DELETE (Client::new_sync must be called outside any async runtime)
266//! let _: Item = client.delete("/items/1").send_sync()?;
267//! # Ok(())
268//! # }
269//! ```
270//!
271//! VEIL-encrypted requests are available via
272//! [`socket::client::RequestBuilder::encryption`] and
273//! [`socket::client::RequestBuilder::encrypted_query`].
274//! The body or query parameters are sealed before the wire send; the response is
275//! opened automatically.
276//!
277//! ### `#[request]` attribute macro
278//!
279//! The [`socket::client::request`] attribute macro is a concise alternative to
280//! the builder calls above. It replaces the decorated `fn` in-place with a `let`
281//! binding that performs the HTTP request. The **function name** becomes the
282//! binding name; the **return type** becomes `R` in the `.send::<R>()` turbofish.
283//! The function body is discarded. A return type annotation is **required**.
284//!
285//! All five builder modes are supported: plain, `json`, `query`, `encrypted`,
286//! and `encrypted_query`. Each mode accepts either `async` (`.send::<R>().await?`)
287//! or `sync` (`.send_sync::<R>()?`).
288//!
289//! ```rust,ignore
290//! use toolkit_zero::socket::client::{Client, Target, request};
291//! use serde::{Deserialize, Serialize};
292//!
293//! #[derive(Deserialize, Serialize, Clone)]
294//! struct Item { id: u32, name: String }
295//!
296//! #[derive(Serialize)]
297//! struct NewItem { name: String }
298//!
299//! # async fn run() -> Result<(), reqwest::Error> {
300//! let client = Client::new_async(Target::Localhost(8080));
301//!
302//! // Plain async GET
303//! #[request(client, GET, "/items", async)]
304//! async fn items() -> Vec<Item> {}
305//!
306//! // POST with JSON body
307//! #[request(client, POST, "/items", json(NewItem { name: "widget".into() }), async)]
308//! async fn created() -> Item {}
309//!
310//! // Synchronous DELETE
311//! #[request(client, DELETE, "/items/1", sync)]
312//! fn deleted() -> Item {}
313//! # Ok(())
314//! # }
315//! ```
316//!
317//! See [`socket::client::request`] for the full syntax reference.
318//!
319//! ---
320//!
321//! ## Location
322//!
323//! The `location` (or `location-browser`) feature provides browser-based geographic
324//! coordinate acquisition. A temporary HTTP server is bound on a randomly assigned
325//! local port, the system default browser is directed to a consent page, and the
326//! coordinates are submitted via the standard Web Geolocation API. The server
327//! shuts itself down upon receiving a result.
328//!
329//! Two entry points are available in [`location::browser`]:
330//!
331//! | Function | Context |
332//! |---|---|
333//! | [`location::browser::__location__`] | Blocking — safe from sync or async |
334//! | [`location::browser::__location_async__`] | Async — preferred inside `#[tokio::main]` |
335//!
336//! ```rust,ignore
337//! use toolkit_zero::location::browser::{__location__, __location_async__, PageTemplate};
338//!
339//! // Blocking — works from sync main or from inside a Tokio runtime
340//! match __location__(PageTemplate::default()) {
341//!     Ok(data) => println!("lat={:.6}  lon={:.6}  ±{:.0}m",
342//!                          data.latitude, data.longitude, data.accuracy),
343//!     Err(e)   => eprintln!("location error: {e}"),
344//! }
345//!
346//! // Async — preferred when already inside #[tokio::main]
347//! # async fn run() {
348//! match __location_async__(PageTemplate::default()).await {
349//!     Ok(data) => println!("lat={:.6}  lon={:.6}", data.latitude, data.longitude),
350//!     Err(e)   => eprintln!("location error: {e}"),
351//! }
352//! # }
353//! ```
354//!
355//! The [`location::browser::PageTemplate`] enum controls what the user sees:
356//! a plain single-button page, a checkbox-gated variant, or a fully custom HTML
357//! document.
358//!
359//! ### `#[browser]` attribute macro
360//!
361//! The [`location::browser::browser`] attribute macro is a concise alternative
362//! to calling `__location__` / `__location_async__` directly. It replaces the
363//! decorated `fn` item with an inline location-capture statement; the function
364//! **name** becomes the binding that holds the resulting
365//! [`location::browser::LocationData`], and the [`location::browser::PageTemplate`]
366//! is built from the macro arguments.
367//!
368//! ```rust,ignore
369//! use toolkit_zero::location::browser::{browser, LocationData, LocationError};
370//!
371//! async fn get_location() -> Result<LocationData, LocationError> {
372//!     // async, plain Default template
373//!     #[browser]
374//!     fn loc() {}
375//!     Ok(loc)
376//! }
377//!
378//! async fn get_location_tickbox() -> Result<LocationData, LocationError> {
379//!     // async, Tickbox with consent text
380//!     #[browser(tickbox, title = "Verify Location", consent = "I agree")]
381//!     fn loc() {}
382//!     Ok(loc)
383//! }
384//!
385//! fn get_location_sync() -> Result<LocationData, LocationError> {
386//!     // blocking, custom title
387//!     #[browser(sync, title = "My App")]
388//!     fn loc() {}
389//!     Ok(loc)
390//! }
391//! ```
392//!
393//! See [`location::browser::browser`] for the full argument reference.
394//!
395//! ---
396//!
397//! ## Encryption — Timelock
398//!
399//! The `encryption` feature (or any `enc-timelock-*` sub-feature) exposes a
400//! **time-locked key derivation** scheme.  A 32-byte key is derived from a
401//! time string through a three-pass KDF chain:
402//!
403//! > **Argon2id** (pass 1) → **scrypt** (pass 2) → **Argon2id** (pass 3)
404//!
405//! The key is reproducible only when the same time value, precision, format,
406//! and salts are supplied. An additional passphrase may be incorporated for a
407//! combined time × passphrase security model.
408//!
409//! **Features:**
410//!
411//! | Feature | Enables |
412//! |---|---|
413//! | `enc-timelock-keygen-now` | [`encryption::timelock::timelock`]`(None)` — decryption path (key from system clock) |
414//! | `enc-timelock-keygen-input` | [`encryption::timelock::timelock`]`(Some(t))` — encryption path (key from explicit time) |
415//! | `enc-timelock-async-keygen-now` | [`encryption::timelock::timelock_async`]`(None)` — async decryption path |
416//! | `enc-timelock-async-keygen-input` | [`encryption::timelock::timelock_async`]`(Some(t))` — async encryption path |
417//! | `encryption` | All four of the above |
418//!
419//! **Presets:** [`encryption::timelock::KdfPreset`] provides named parameter sets
420//! tuned per platform: `Balanced`, `Paranoid`, `BalancedMac`, `ParanoidMac`,
421//! `BalancedX86`, `ParanoidX86`, `BalancedArm`, `ParanoidArm`, and `Custom(KdfParams)`.
422//!
423//! ```rust,ignore
424//! use toolkit_zero::encryption::timelock::*;
425//!
426//! // Encryption side — caller sets the unlock time
427//! let salts = TimeLockSalts::generate();
428//! let kdf   = KdfPreset::BalancedMac.params();
429//! let at    = TimeLockTime::new(14, 30).unwrap();
430//! // params = None → _at (encryption) path
431//! let enc_key = timelock(
432//!     Some(TimeLockCadence::None),
433//!     Some(at),
434//!     Some(TimePrecision::Minute),
435//!     Some(TimeFormat::Hour24),
436//!     Some(salts.clone()),
437//!     Some(kdf),
438//!     None,
439//! ).unwrap();
440//!
441//! // Pack all settings (incl. salts + KDF params) into a self-contained header;
442//! // store it in the ciphertext — salts and KDF params are not secret.
443//! let header = pack(TimePrecision::Minute, TimeFormat::Hour24,
444//!                   &TimeLockCadence::None, salts, kdf);
445//!
446//! // Decryption side — load header from ciphertext; call at 14:30 local time.
447//! // params = Some(header) → _now (decryption) path
448//! let dec_key = timelock(
449//!     None, None, None, None, None, None,
450//!     Some(header),
451//! ).unwrap();
452//! // enc_key.as_bytes() == dec_key.as_bytes() when called at 14:30 local time
453//! ```
454//!
455//! ---
456//!
457//! ## Dependency Graph — BuildTimeFingerprint
458//!
459//! Two features; one for each side of the boundary.
460//!
461//! **`dependency-graph-build`** (goes in `[build-dependencies]`):
462//! [`dependency_graph::build::generate_fingerprint`] runs `cargo metadata`, hashes
463//! `Cargo.lock` and every `.rs` file under `src/`, captures the profile, target
464//! triple, rustc version, and active features, then writes a compact, normalised
465//! JSON document to `$OUT_DIR/fingerprint.json`.
466//! [`dependency_graph::build::export`] optionally writes a pretty-printed copy
467//! alongside `Cargo.toml` for local inspection — pass `false` or
468//! `cfg!(debug_assertions)` to suppress it in release builds.
469//!
470//! **`dependency-graph-capture`** (goes in `[dependencies]`):
471//! [`dependency_graph::capture::parse`] deserialises the embedded snapshot into a
472//! typed [`dependency_graph::capture::BuildTimeFingerprintData`] struct.
473//! [`dependency_graph::capture::as_bytes`] returns the raw JSON bytes, which are
474//! stable and deterministic across equivalent builds.
475//!
476//! ### `#[dependencies]` attribute macro
477//!
478//! The [`dependency_graph::capture::dependencies`] attribute macro is a concise
479//! alternative to the manual `include_str!` + `parse()` boilerplate. Apply it to
480//! an empty `fn`; the function name becomes the binding.
481//!
482//! ```rust,ignore
483//! use toolkit_zero::dependency_graph::capture::dependencies;
484//!
485//! fn show() -> Result<(), Box<dyn std::error::Error>> {
486//!     #[dependencies]             // → let data: BuildTimeFingerprintData = parse(...)?;
487//!     fn data() {}
488//!     println!("{} v{}", data.package.name, data.package.version);
489//!     Ok(())
490//! }
491//!
492//! fn raw() -> &'static [u8] {
493//!     #[dependencies(bytes)]      // → let raw: &'static [u8] = as_bytes(...);
494//!     fn raw() {}
495//!     raw
496//! }
497//! ```
498//!
499//! See [`dependency_graph::capture::dependencies`] for the full syntax reference.
500//!
501//! ### Sections captured in `fingerprint.json`
502//!
503//! | Section | Contents |
504//! |---|---|
505//! | `package` | crate name + version |
506//! | `build` | profile, opt-level, target triple, rustc version, active feature flags |
507//! | `deps` | full normalised `cargo metadata` graph (sorted, no absolute paths) |
508//! | `cargo_lock_sha256` | SHA-256 of `Cargo.lock` (comment lines stripped) |
509//! | `source` | SHA-256 of every `.rs` file under `src/` |
510//!
511//! ### Setup
512//!
513//! ```toml
514//! [dependencies]
515//! toolkit-zero = { version = "3", features = ["dependency-graph-capture"] }
516//!
517//! [build-dependencies]
518//! toolkit-zero = { version = "3", features = ["dependency-graph-build"] }
519//! ```
520//!
521//! `build.rs`:
522//!
523//! ```rust,ignore
524//! fn main() {
525//!     toolkit_zero::dependency_graph::build::generate_fingerprint()
526//!         .expect("fingerprint generation failed");
527//!     // optional: pretty-print to crate root for local inspection
528//!     toolkit_zero::dependency_graph::build::export(cfg!(debug_assertions))
529//!         .expect("fingerprint export failed");
530//! }
531//! ```
532//!
533//! `src/main.rs` (or any binary):
534//!
535//! ```rust,ignore
536//! use toolkit_zero::dependency_graph::capture;
537//!
538//! const BUILD_TIME_FINGERPRINT: &str = include_str!(concat!(env!("OUT_DIR"), "/fingerprint.json"));
539//!
540//! fn main() {
541//!     let data = capture::parse(BUILD_TIME_FINGERPRINT).expect("failed to parse fingerprint");
542//!     println!("{} v{}", data.package.name, data.package.version);
543//!     println!("target : {}", data.build.target);
544//!     println!("lock   : {}", data.cargo_lock_sha256);
545//!
546//!     let raw = capture::as_bytes(BUILD_TIME_FINGERPRINT);
547//!     println!("{} bytes", raw.len());
548//! }
549//! ```
550//!
551//! ### Risks and considerations
552//!
553//! * **Not tamper-proof** — the fingerprint is embedded as plain text in the
554//!   binary's read-only data section and is readable by anyone with access to
555//!   the binary. It is informational in nature; it does not constitute a
556//!   security boundary.
557//! * **Export file** — `export(true)` writes `fingerprint.json` to the crate root.
558//!   Add it to `.gitignore` to prevent accidental commits.
559//! * **Build-time overhead** — `cargo metadata` runs on every rebuild triggered
560//!   by the `cargo:rerun-if-changed` directives (changes to `src/`, `Cargo.toml`,
561//!   or `Cargo.lock`).
562//! * **Feature scope** — `build.features` captures active features of the crate
563//!   being built, not toolkit-zero's own features.
564//! * **Path stripping** — absolute and machine-specific paths are removed from
565//!   `cargo metadata` output so the fingerprint is stable across machines.
566//! * **Compile-time only** — the snapshot reflects the build environment at
567//!   compile time; it does not change at runtime.
568//!
569//! ---
570//!
571//! ## Backend deps
572//!
573//! The `backend-deps` feature appends a `backend_deps` sub-module to each active
574//! module. Every such sub-module re-exports via `pub use` all third-party crates
575//! used internally by the parent module, allowing downstream crates to access
576//! those dependencies without separate `Cargo.toml` declarations.
577//!
578//! | Module | Path | Re-exports |
579//! |---|---|---|
580//! | serialization | [`serialization::backend_deps`] | `bincode`, `base64`, `zeroize` |
581//! | socket (server) | [`socket::backend_deps`] | `bincode`, `base64`, `serde`, `tokio`, `log`, `bytes`, `serde_urlencoded`, `warp` |
582//! | socket (client) | [`socket::backend_deps`] | `bincode`, `base64`, `serde`, `tokio`, `log`, `reqwest` |
583//! | location | [`location::backend_deps`] | `tokio`, `serde`, `webbrowser`, `rand` |
584//! | encryption (timelock) | [`encryption::timelock::backend_deps`] | `argon2`, `scrypt`, `zeroize`, `chrono`, `rand`; `tokio` (async variants only) |
585//! | dependency_graph | [`dependency_graph::backend_deps`] | `serde_json`; `sha2` (build side only) |
586//!
587//! Enabling `backend-deps` without any other feature compiles successfully but
588//! exposes no symbols; every re-export within `backend_deps` is individually
589//! gated on the corresponding parent feature.
590
591#[cfg(any(feature = "socket", feature = "socket-server", feature = "socket-client"))]
592pub mod socket;
593
594#[cfg(any(feature = "location", feature = "location-browser"))]
595pub mod location;
596
597#[cfg(feature = "serialization")]
598pub mod serialization;
599
600#[cfg(any(feature = "encryption", feature = "enc-timelock-keygen-now", feature = "enc-timelock-keygen-input", feature = "enc-timelock-async-keygen-now", feature = "enc-timelock-async-keygen-input"))]
601pub mod encryption;
602
603#[cfg(any(feature = "dependency-graph-build", feature = "dependency-graph-capture"))]
604#[path = "dependency-graph/mod.rs"]
605pub mod dependency_graph;