Skip to main content

modo/
lib.rs

1//! # modo
2//!
3//! A Rust web framework for small monolithic apps. Single crate, zero proc
4//! macros, built on [`axum`] 0.8 with [`libsql`](https://crates.io/crates/libsql)
5//! (SQLite) for persistence. Handlers are plain `async fn`, routes use
6//! [`axum::Router`] directly, services are wired explicitly in `main()`, and
7//! database queries use raw libsql. Current version: **0.11.0**.
8//!
9//! ## Modules
10//!
11//! Foundation:
12//!
13//! | Module | Purpose |
14//! |--------|---------|
15//! | [`config`] | YAML config loader with `${VAR}` / `${VAR:default}` env substitution |
16//! | [`error`] | Framework [`Error`] type (status + message + source + code) and [`Result`] alias |
17//! | [`runtime`] | Graceful-shutdown [`Task`](runtime::Task) abstraction used by [`run!`] |
18//! | [`server`] | HTTP server bootstrap with bind, listen, and shutdown wiring |
19//! | [`service`] | Typed service [`Registry`](service::Registry) and [`AppState`](service::AppState) |
20//!
21//! Persistence & I/O:
22//!
23//! | Module | Purpose |
24//! |--------|---------|
25//! | [`cache`] | In-memory LRU cache with TTL |
26//! | [`db`] | libsql/SQLite [`Database`](db::Database) handle, migrations, query helpers |
27//! | [`storage`] | S3-compatible object storage with ACL and upload-from-URL |
28//!
29//! Request lifecycle:
30//!
31//! | Module | Purpose |
32//! |--------|---------|
33//! | [`client`] | HTTP client with shared connection pool |
34//! | [`cookie`] | Signed/private cookie helpers and config |
35//! | [`extractor`] | Request extractors with auto-sanitisation (JSON, form, query, multipart) |
36//! | [`flash`] | Signed read-once cookie flash messages |
37//! | [`ip`] | [`ClientIp`](ip::ClientIp) extractor with trusted-proxy resolution |
38//! | [`middleware`] | Tower middleware (CORS, CSRF, rate limit, security headers, etc.) |
39//! | [`sse`] | Server-Sent Events with named broadcast channels |
40//!
41//! Identity & multi-tenancy:
42//!
43//! | Module | Purpose |
44//! |--------|---------|
45//! | [`auth`] | Sessions (cookie + JWT), passwords, TOTP, OAuth2, API keys, RBAC roles |
46//! | [`tenant`] | Multi-tenancy via subdomain, header, path, or custom resolver |
47//! | [`tier`] | Subscription-tier gating |
48//!
49//! Background work:
50//!
51//! | Module | Purpose |
52//! |--------|---------|
53//! | [`cron`] | 5/6-field cron scheduler |
54//! | [`job`] | SQLite-backed job queue with retries, scheduling, and idempotent enqueue |
55//!
56//! Application services:
57//!
58//! | Module | Purpose |
59//! |--------|---------|
60//! | [`email`] | Markdown-to-HTML email rendering with SMTP |
61//! | [`i18n`] | ICU plural rules, locale resolution, translation store |
62//! | [`qrcode`] | QR code rendering (SVG / PNG) |
63//! | [`template`] | MiniJinja with i18n, HTMX detection, flash integration |
64//! | [`webhook`] | Outbound webhook delivery with Standard Webhooks signing |
65//!
66//! Observability:
67//!
68//! | Module | Purpose |
69//! |--------|---------|
70//! | [`audit`] | Structured audit-log writer |
71//! | [`health`] | `/_live` and `/_ready` endpoint handlers |
72//! | [`tracing`] | Tracing/Sentry subscriber setup and request-trace middleware |
73//!
74//! Network primitives:
75//!
76//! | Module | Purpose |
77//! |--------|---------|
78//! | [`dns`] | TXT/CNAME verification for custom-domain validation |
79//! | [`embed`] | Embed.ly-style URL preview |
80//! | [`geolocation`] | MaxMind GeoIP2 lookup with middleware |
81//!
82//! Utilities:
83//!
84//! | Module | Purpose |
85//! |--------|---------|
86//! | [`encoding`] | Base64url, hex, and other encoding helpers |
87//! | [`id`] | [`id::ulid()`](id::ulid) (26-char) and [`id::short()`](id::short) (13-char base36) |
88//! | [`sanitize`] | [`Sanitize`](sanitize::Sanitize) trait used by extractors |
89//! | [`validate`] | [`Validate`](validate::Validate), [`Validator`](validate::Validator), [`ValidationError`](validate::ValidationError) |
90//!
91//! Virtual flat-indexes (cross-module re-exports for discoverability):
92//!
93//! | Module | Purpose |
94//! |--------|---------|
95//! | [`extractors`] | Every public request extractor, gathered from across the crate |
96//! | [`guards`] | Every route-level gating layer applied via `.route_layer()` |
97//! | [`middlewares`] | Every public middleware constructor |
98//! | [`prelude`] | Glob-import bundle for handler modules |
99//!
100//! Test-only (gated behind the `test-helpers` feature):
101//!
102//! | Module | Purpose |
103//! |--------|---------|
104//! | `testing` | `TestDb`, `TestApp`, `TestSession`, and in-memory/stub backends |
105//!
106//! ## Re-exports
107//!
108//! Crate-root re-exports for the items used in almost every program:
109//!
110//! - [`Error`] — framework error type (HTTP status + message + optional source/code)
111//! - [`Result`] — `std::result::Result<T, Error>` alias
112//! - [`Config`] — top-level application configuration ([`config::Config`])
113//! - [`run!`] — macro that waits for SIGTERM/SIGINT then shuts down each
114//!   supplied [`Task`](runtime::Task) in declaration order
115//!
116//! Plus the four dependency crates whose types appear in handler signatures, so
117//! you don't need to pin matching versions yourself:
118//!
119//! - [`axum`] — router, extractors, responses
120//! - [`serde`] — `Serialize` / `Deserialize` derives
121//! - [`serde_json`] — JSON values and macros
122//! - [`tokio`] — runtime, tasks, sync primitives
123//!
124//! ## Quick start
125//!
126//! Add to `Cargo.toml`:
127//!
128//! ```toml
129//! [dependencies]
130//! modo = { package = "modo-rs", version = "0.11.0" }
131//!
132//! [dev-dependencies]
133//! modo = { package = "modo-rs", version = "0.11.0", features = ["test-helpers"] }
134//! ```
135//!
136//! Minimal application:
137//!
138//! ```rust,no_run
139//! use modo::axum::{Router, routing::get};
140//! use modo::{Config, Result};
141//!
142//! async fn hello() -> &'static str {
143//!     "Hello, modo!"
144//! }
145//!
146//! #[tokio::main]
147//! async fn main() -> Result<()> {
148//!     let config: Config = modo::config::load("config/")?;
149//!     let app = Router::new().route("/", get(hello));
150//!     let server = modo::server::http(app, &config.server).await?;
151//!     modo::run!(server).await
152//! }
153//! ```
154//!
155//! Inside a handler module, pull in the common handler-time types with:
156//!
157//! ```ignore
158//! use modo::prelude::*;
159//! ```
160//!
161//! ## Features
162//!
163//! Every production module is always compiled — there are no per-capability
164//! cargo features. The only flag is `test-helpers`, which exposes in-memory
165//! backends and test harnesses; enable it in your `[dev-dependencies]`.
166//!
167//! | Feature | Purpose |
168//! |---------|---------|
169//! | `test-helpers` | Enables the `testing` module (`TestDb`, `TestApp`, `TestSession`) and all in-memory/stub backends |
170
171pub mod config;
172pub mod error;
173pub mod runtime;
174pub mod server;
175pub mod service;
176
177pub mod cache;
178pub mod db;
179pub mod storage;
180
181pub mod client;
182pub mod cookie;
183pub mod extractor;
184pub mod flash;
185pub mod ip;
186pub mod middleware;
187pub mod sse;
188
189pub mod auth;
190pub mod tenant;
191pub mod tier;
192
193pub mod cron;
194pub mod job;
195
196pub mod email;
197pub mod i18n;
198pub mod qrcode;
199pub mod template;
200pub mod webhook;
201
202pub mod audit;
203pub mod health;
204pub mod tracing;
205
206pub mod dns;
207pub mod embed;
208pub mod geolocation;
209
210pub mod encoding;
211pub mod id;
212pub mod sanitize;
213pub mod validate;
214
215#[cfg(feature = "test-helpers")]
216pub mod testing;
217
218pub mod extractors;
219pub mod guards;
220pub mod middlewares;
221pub mod prelude;
222
223/// Top-level framework configuration deserialised from YAML — see [`config::Config`].
224pub use config::Config;
225/// Framework error type and `Result<T, Error>` alias — see [`error::Error`] / [`error::Result`].
226pub use error::{Error, Result};
227
228/// Re-export of the [`axum`] crate so downstream apps don't need to pin a matching version.
229pub use axum;
230/// Re-export of [`serde`] for `Serialize` / `Deserialize` derives in handler types.
231pub use serde;
232/// Re-export of [`serde_json`] for JSON values, the `json!` macro, and `Value`.
233pub use serde_json;
234/// Re-export of the [`tokio`] runtime — tasks, sync primitives, time, signals.
235pub use tokio;