hyperlite 0.1.0

Lightweight HTTP framework built on hyper, tokio, and tower
Documentation
#![allow(dead_code)]

//! # Hyperlite
//!
//! Hyperlite is a lightweight HTTP framework built on top of `hyper`, `tokio`, and
//! `tower` for building fast, composable web services without sacrificing control.
//!
//! ## Philosophy
//! - Stay close to `hyper` primitives for maximum flexibility.
//! - Embrace Tower's `Service` trait for middleware-first composition.
//! - Provide type-safe request and response helpers without macros.
//! - Favor zero-cost abstractions over hidden magic.
//!
//! ## Key Features
//! - Fast path-based routing powered by [`matchit`](https://docs.rs/matchit).
//! - Tower middleware compatibility, including tracing, CORS, and authentication layers.
//! - Ergonomic response builders and JSON helpers.
//! - Built-in server runner with graceful shutdown handling.
//! - Focus on predictable upgrades by avoiding breaking framework changes.
//! - ✅ Type-safe request extraction helpers for bodies, queries, paths, and extensions.
//!
//! ## Quick Example
//! ```rust,no_run
//! use bytes::Bytes;
//! use hyper::{Method, Request, Response, StatusCode};
//! use hyperlite::{serve, success, BoxBody, Router};
//! use http_body_util::Full;
//! use serde::Serialize;
//! use std::net::SocketAddr;
//! use std::sync::Arc;
//!
//! #[derive(Clone)]
//! struct AppState;
//!
//! #[derive(Serialize)]
//! struct Greeting {
//!     message: String,
//! }
//!
//! async fn hello_handler(
//!     _req: Request<BoxBody>,
//!     _state: Arc<AppState>,
//! ) -> Result<Response<Full<Bytes>>, hyperlite::BoxError> {
//!     Ok(success(
//!         StatusCode::OK,
//!         Greeting {
//!             message: "Hello, World!".to_string(),
//!         },
//!     ))
//! }
//!
//! #[tokio::main]
//! async fn main() -> Result<(), hyperlite::BoxError> {
//!     let state = AppState;
//!     let router = Router::new(state).route(
//!         "/hello",
//!         Method::GET,
//!         Arc::new(|req, state| Box::pin(hello_handler(req, state))),
//!     );
//!
//!     let addr: SocketAddr = "127.0.0.1:3000".parse().unwrap();
//!     serve(addr, router).await
//! }
//! ```
//!
//! For richer walkthroughs, inspect the `examples/` directory:
//! - `hello_world.rs` – Minimal single-route server.
//! - `with_state.rs` – Shared state with all extractors.
//! - `with_middleware.rs` – Complete Tower middleware stack.
//!
//! Run any example via `cargo run --example <name>`.
//!
//! ## Request Extraction
//! ```rust,no_run
//! use bytes::Bytes;
//! use hyper::{Method, Request, Response, StatusCode};
//! use hyperlite::{parse_json_body, path_param, query_params, BoxBody, BoxError, Router, success};
//! use http_body_util::Full;
//! use serde::{Deserialize, Serialize};
//! use std::sync::Arc;
//! use uuid::Uuid;
//!
//! #[derive(Clone)]
//! struct AppState;
//!
//! #[derive(Deserialize)]
//! struct CreatePost { title: String, body: String }
//!
//! #[derive(Deserialize)]
//! struct Pagination { page: u32, limit: u32 }
//!
//! #[derive(Serialize)]
//! struct Post { id: Uuid, title: String }
//!
//! async fn create_post(
//!     req: Request<BoxBody>,
//!     _state: Arc<AppState>,
//! ) -> Result<Response<Full<Bytes>>, BoxError> {
//!     let payload = parse_json_body::<CreatePost>(req).await?;
//!     Ok(success(StatusCode::CREATED, Post {
//!         id: Uuid::new_v4(),
//!         title: payload.title,
//!     }))
//! }
//!
//! async fn list_posts(
//!     req: Request<BoxBody>,
//!     _state: Arc<AppState>,
//! ) -> Result<Response<Full<Bytes>>, BoxError> {
//!     let params = query_params::<Pagination>(&req)?;
//!     Ok(success(StatusCode::OK, format!("page={}", params.page)))
//! }
//!
//! async fn get_post(
//!     req: Request<BoxBody>,
//!     _state: Arc<AppState>,
//! ) -> Result<Response<Full<Bytes>>, BoxError> {
//!     let post_id: Uuid = path_param(&req, "id")?;
//!     Ok(success(StatusCode::OK, Post { id: post_id, title: "Example".into() }))
//! }
//!
//! #[tokio::main]
//! # async fn main() -> Result<(), BoxError> {
//!     let state = AppState;
//!     let router = Router::new(state)
//!         .route("/posts", Method::POST, Arc::new(|req, state| Box::pin(create_post(req, state))))
//!         .route("/posts", Method::GET, Arc::new(|req, state| Box::pin(list_posts(req, state))))
//!         .route("/posts/{id}", Method::GET, Arc::new(|req, state| Box::pin(get_post(req, state))));
//!
//!     let addr: std::net::SocketAddr = "127.0.0.1:3000".parse().unwrap();
//!     hyperlite::serve(addr, router).await
//! }
//! ```
//!
//! See `examples/with_state.rs` for a full application using these helpers in
//! tandem with shared state and realistic routing patterns.
//!
//! ## Examples
//!
//! ```text
//! cargo run --example hello_world
//! cargo run --example with_state
//! RUST_LOG=info cargo run --example with_middleware
//! ```
//!
//! Each example demonstrates progressively more advanced patterns, from simple
//! routing to full middleware stacks with distributed tracing.
//!
//! ## When To Use
//! Choose Hyperlite when you want the ergonomics of a framework while keeping
//! full control over the underlying HTTP server and middleware stack.
//!
// Planned modules to be implemented in future phases.
pub mod extract;
pub mod response;
pub mod router;
pub mod server;

pub use extract::{get_extension, parse_json_body, path_param, path_params, query_params};
pub use response::{
    empty, failure, not_found, success, with_correlation_id, ApiError, ResponseBody,
};
pub use router::{BodyError, BoxBody, Handler, PathParams, Router};
pub use server::serve;

/// Boxed error type used across Hyperlite.
pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;

/// Convenience result type used by Hyperlite components.
pub type Result<T> = std::result::Result<T, BoxError>;