Skip to main content

rust_api/
lib.rs

1//! RustAPI: FastAPI-inspired REST framework for Rust
2//!
3//! RustAPI brings the developer experience of FastAPI and NestJS to Rust,
4//! with automatic OpenAPI generation, built-in validation, and dependency
5//! injection.
6//!
7//! # Features
8//!
9//! - **Route Macros**: Define endpoints with `#[get]`, `#[post]`, etc. The HTTP
10//!   verb from the annotation is a binding contract — enforced at registration
11//!   time via the `mount_handlers!` macro.
12//! - **Kleisli Pipeline**: `RouterPipeline` composes `Controller` Kleisli
13//!   arrows with `and_then` (`>>=`), short-circuiting on any error.
14//! - **Direct DI**: Pass `Arc<Service>` directly to `pipeline.mount::<C>(svc)`.
15//!   No type-map registry required for the primary use case.
16//! - **Type-Driven**: Leverage Rust's type system for validation and docs.
17//! - **Zero-Cost**: Built on Axum and Tokio for production performance.
18//!
19//! # Quick Start
20//!
21//! ```ignore
22//! use rust_api::prelude::*;
23//!
24//! pub struct UserController;
25//!
26//! #[get("/users")]
27//! pub async fn list_users(State(svc): State<Arc<UserService>>) -> Json<Vec<User>> {
28//!     Json(svc.list())
29//! }
30//!
31//! mount_handlers!(UserController, UserService, [(__list_users_route, list_users)]);
32//!
33//! #[tokio::main]
34//! async fn main() {
35//!     let svc = Arc::new(UserService::new());
36//!
37//!     let app = RouterPipeline::new()
38//!         .mount::<UserController>(svc)
39//!         .map(|r| r.layer(TraceLayer::new_for_http()))
40//!         .build()
41//!         .unwrap();
42//!
43//!     RustAPI::new(app).port(3000).serve().await.unwrap();
44//! }
45//! ```
46
47// Core modules
48pub mod app;
49pub mod controller;
50pub mod di;
51pub mod error;
52pub mod middleware;
53pub mod pipeline;
54pub mod router;
55pub mod server;
56
57// Re-export core types
58pub use app::App;
59pub use controller::Controller;
60pub use di::{Container, Injectable};
61pub use error::{Error, Result};
62pub use middleware::{guard, require_bearer};
63pub use pipeline::{RouterPipeline, RouterTransform};
64pub use router::{method_filter_from_str, ApiRoute, Router, RouterExt};
65pub use server::RustAPI;
66
67// Re-export routing methods from Axum (available for advanced use)
68pub mod routing {
69    pub use axum::routing::*;
70}
71
72// Re-export commonly used axum types.
73// Clients should never need to add `axum` as a direct dependency — all
74// surface types required to write handlers, extractors, and middleware
75// are available through this crate.
76pub use std::sync::Arc;
77
78pub use axum::{
79    extract::{FromRequestParts, Path, Query, State},
80    http::{header, request::Parts, StatusCode},
81    response::{IntoResponse, Response},
82    Json,
83};
84// Re-export macros
85pub use rust_api_macros::{delete, get, patch, post, put};
86// Re-export serde for user convenience
87pub use serde::{Deserialize, Serialize};
88pub use tower_http::{cors::CorsLayer, trace::TraceLayer};
89
90/// Generate a [`Controller`] implementation for a type from a handler list.
91///
92/// This macro produces the Kleisli arrow `Arc<State> -> (Router ->
93/// Result<Router>)` that the `RouterPipeline::mount` method threads through the
94/// pipeline.
95///
96/// Controllers using this macro have **zero dependency on `Router`, `RouteSet`,
97/// or any DI container** in their source files.
98///
99/// # Syntax
100///
101/// ```ignore
102/// mount_handlers!(ControllerType, ServiceType, [
103///     (__route_constant_1, handler_fn_1),
104///     (__route_constant_2, handler_fn_2),
105/// ]);
106/// ```
107///
108/// Where each `__route_constant` is the tuple `(&'static str, &'static str)`
109/// generated by a `#[get]`/`#[post]`/etc. annotation on the handler function.
110///
111/// # Example
112///
113/// ```ignore
114/// pub struct HealthController;
115///
116/// #[get("/health")]
117/// pub async fn health_check(State(svc): State<Arc<HealthService>>) -> Json<HealthResponse> {
118///     Json(svc.health_check())
119/// }
120///
121/// mount_handlers!(HealthController, HealthService, [
122///     (__health_check_route, health_check),
123/// ]);
124/// ```
125///
126/// Then in `main.rs`:
127///
128/// ```ignore
129/// RouterPipeline::new()
130///     .mount::<HealthController>(Arc::new(HealthService::new()))
131///     .build()?
132/// ```
133#[macro_export]
134macro_rules! mount_handlers {
135    // Entry: ControllerType, StateType, [ (route_const, handler), ... ]
136    ($controller:ty, $state:ty, [ $( ($route:expr, $handler:expr) ),* $(,)? ]) => {
137        impl $crate::controller::Controller for $controller {
138            type State = $state;
139
140            fn mount(
141                state: ::std::sync::Arc<$state>,
142            ) -> impl ::std::ops::FnOnce(
143                $crate::router::Router<()>,
144            ) -> $crate::error::Result<$crate::router::Router<()>> {
145                move |router| {
146                    // Build a scoped router<Arc<State>> with one .route() per handler.
147                    // The HTTP verb is read from the route constant — it is the
148                    // annotation-enforced contract and cannot be overridden here.
149                    let scoped: $crate::router::Router<::std::sync::Arc<$state>> =
150                        $crate::router::Router::new()
151                        $(
152                            .route(
153                                $route.0,
154                                $crate::routing::on(
155                                    $crate::router::method_filter_from_str($route.1),
156                                    $handler,
157                                ),
158                            )
159                        )*;
160                    // Provide state (Router<Arc<State>> -> Router<()>) and merge.
161                    Ok(router.merge(scoped.with_state(state)))
162                }
163            }
164        }
165    };
166}
167
168/// Prelude module for convenient imports.
169///
170/// Import everything you need with:
171/// ```ignore
172/// use rust_api::prelude::*;
173/// ```
174///
175/// # DI Note
176///
177/// [`Container`](crate::di::Container) is intentionally **not** in the prelude.
178/// The primary DI mechanism is direct `Arc<Service>` passing to
179/// `RouterPipeline::mount`. Import `Container` explicitly if you need a
180/// registry for large apps: `use rust_api::di::Container;`
181pub mod prelude {
182    pub use tokio;
183
184    pub use super::{
185        // Macros
186        delete,
187        get,
188        guard,
189        header,
190        patch,
191        post,
192        put,
193        require_bearer,
194        router,
195        // Router
196        ApiRoute,
197        App,
198        // Core types
199        Arc,
200        // Controller trait — visible for doc purposes; mount_handlers! generates impls.
201        Controller,
202        // Middleware
203        CorsLayer,
204        Deserialize,
205        Error,
206        // Axum — all surface types needed to write handlers and custom extractors.
207        // Clients must never add `axum` as a direct dependency.
208        FromRequestParts,
209        IntoResponse,
210        Json,
211        Parts,
212        Path,
213        Query,
214        Response,
215        Result,
216        Router,
217        RouterExt,
218        // Pipeline
219        RouterPipeline,
220        RouterTransform,
221        RustAPI,
222        // Serde
223        Serialize,
224        State,
225        StatusCode,
226        TraceLayer,
227    };
228    // Re-export the mount_handlers! macro so `use rust_api::prelude::*` brings it in.
229    pub use crate::mount_handlers;
230}