xitca_web/
lib.rs

1//! a web framework focus on memory efficiency, composability, and fast compile time.
2//!
3//! # Quick start
4//! ```no_run
5//! use xitca_web::{handler::handler_service, route::get, App};
6//!
7//! fn main() -> std::io::Result<()> {
8//!     App::new()
9//!         .at("/", get(handler_service(|| async { "Hello,World!" })))
10//!         .serve()
11//!         .bind("localhost:8080")?
12//!         .run()
13//!         .wait()
14//! }
15//! ```
16//!
17//! # Memory efficient
18//! - zero copy magic types
19//! - zero cost service tree
20//!
21//! ## Zero copy
22//! ```rust
23//! # #[cfg(feature = "json")]
24//! # fn _main() -> std::io::Result<()> {
25//! use xitca_web::{
26//!     error::Error,
27//!     handler::{handler_service, json::LazyJson, path::PathRef},
28//!     route::{get, post},
29//!     App
30//! };
31//!
32//! // PathRef is able to borrow http request's path string as reference
33//! // without copying it.
34//! async fn url_query(PathRef(path): PathRef<'_>) -> &'static str {
35//!     println!("{path}");
36//!     "zero copy path"    
37//! }
38//!
39//! // deserializable user type.
40//! #[derive(serde::Deserialize)]
41//! struct User<'a> {
42//!     name: &'a str
43//! }
44//!
45//! // LazyJson is able to lazily deserialize User type with zero copy &str.
46//! async fn json(lazy: LazyJson<User<'_>>) -> Result<&'static str, Error> {
47//!     let User { name } = lazy.deserialize()?;
48//!     println!("{name}");
49//!     Ok("zero copy json")    
50//! }
51//!
52//! // Almost all magic extract types in xitca-web utilize zero copy
53//! // to avoid unnecessary memory copy.
54//! App::new()
55//!     // a route handling incoming url query.
56//!     .at("/query", get(handler_service(url_query)))
57//!     // a route handling incoming json object.
58//!     .at("/json", post(handler_service(json)))
59//!     .serve()
60//!     .bind("localhost:8080")?
61//!     .run()
62//!     .wait()
63//! # }
64//! ```
65//!
66//! ## Zero cost
67//! ```rust
68//! use xitca_web::{
69//!     handler::{handler_service},
70//!     http::WebResponse,
71//!     route::get,
72//!     middleware::Extension,
73//!     service::{Service, ServiceExt},
74//!     App, WebContext
75//! };
76//! # fn _main() -> std::io::Result<()> {
77//! App::new()
78//!     .at("/", get(handler_service(|| async { "hello,world!" })))
79//!     // ServiceExt::enclosed_fn combinator enables async function as middleware.
80//!     // the async function is unboxed and potentially inlined with other async services
81//!     // for efficient binary code with less memory allocation.
82//!     .enclosed_fn(middleware_fn)
83//!     // ServiceExt::enclosed combinator enables type impl Service trait as middleware.
84//!     // the middleware trait method is unboxed and potentially inlined with other async services
85//!     // for efficient binary code with less memory allocation.
86//!     .enclosed(Extension::new(()))
87//!     .serve()
88//!     .bind("localhost:8080")?
89//!     .run()
90//!     .wait()
91//! # }
92//!
93//! // a simple middleware just forward request to inner service logic.
94//! async fn middleware_fn<S, T, E>(service: &S, ctx: WebContext<'_>) -> Result<T, E>
95//! where
96//!     S: for<'r> Service<WebContext<'r>, Response = T, Error = E>
97//! {
98//!     service.call(ctx).await
99//! }
100//! ```
101//!
102//! # Composable
103//! - Easy mixture of various level of abstractions and less opinionated APIs
104//! - Common types and traits for cross crates integration of majority rust web ecosystem
105//!
106//! ## Abstraction variety
107//! ```rust
108//! use xitca_web::{
109//!     body::ResponseBody,
110//!     error::Error,
111//!     handler::{handler_service, handler_sync_service, Responder},
112//!     http::{Method, WebResponse},
113//!     route::get,
114//!     service::fn_service,
115//!     App, WebContext
116//! };
117//!
118//! # fn _main() -> std::io::Result<()> {
119//! App::new()
120//!     // high level abstraction. see fn high for detail.
121//!     .at("/high", get(handler_service(high)))
122//!     // low level abstraction. see fn low for detail.
123//!     .at("/low", get(fn_service(low)))
124//!     // abstraction for synchronous. see fn sync for detail.
125//!     .at("/sync", get(handler_sync_service(sync)))
126//!     .serve()
127//!     .bind("localhost:8080")?
128//!     .run()
129//!     .wait()
130//! # }
131//!
132//! // magic function with arbitrary receiver type and output type
133//! // that can be extracted from http requests and packed into http
134//! // response.
135//! async fn high(method: &Method) -> &'static str {
136//!     // extract http method from http request.
137//!     assert_eq!(method, Method::GET);
138//!     // pack string literal into http response.
139//!     "high level"     
140//! }
141//!
142//! // function with concrete typed input and output where http types
143//! // are handled manually and explicitly.
144//! // it can also be seen as a desugar of previous example of
145//! // handler_service(high)
146//! async fn low(ctx: WebContext<'_>) -> Result<WebResponse, Error> {
147//!     // extract method from request context.
148//!     let method = ctx.extract().await?;
149//!     // execute high level abstraction example function.
150//!     let str = high(method).await;
151//!     // convert string literal to http response.
152//!     str.respond(ctx).await
153//! }
154//!
155//! // high level abstraction but for synchronous function. this function
156//! // is powered by background thread pool so it does not block the async
157//! // code.
158//! fn sync(method: Method) -> &'static str {
159//!     assert_eq!(method, Method::GET);
160//!     // blocking thread for long period of time does not impact xitca-web
161//!     // async internal.
162//!     std::thread::sleep(std::time::Duration::from_secs(3));
163//!     "sync"    
164//! }
165//! ```
166//!
167//! ## Middleware composability
168//! ```rust
169//! use xitca_web::{
170//!     error::Error,
171//!     handler::{handler_service},
172//!     http::WebResponse,
173//!     route::get,
174//!     service::{Service, ServiceExt},
175//!     App, WebContext
176//! };
177//!
178//! # fn _main() -> std::io::Result<()> {
179//! // ServiceExt::enclosed_fn combinator enables async function as middleware.
180//! // in xitca_web almost all service can be enclosed by an middleware.
181//! App::new()
182//!     .at("/",
183//!         get(
184//!             // apply middleware to handler_service
185//!             handler_service(|| async { "hello,world!" })
186//!                 .enclosed_fn(middleware_fn)
187//!         )
188//!         // apply middleware to route
189//!         .enclosed_fn(middleware_fn)
190//!     )
191//!     // apply middleware to application
192//!     .enclosed_fn(middleware_fn)
193//!     .serve()
194//!     .bind("localhost:8080")?
195//!     .run()
196//!     .wait()
197//! # }
198//!
199//! // a simple middleware just forward request to inner service logic.
200//! async fn middleware_fn<S, T, E>(service: &S, ctx: WebContext<'_>) -> Result<T, E>
201//! where
202//!     S: for<'r> Service<WebContext<'r>, Response = T, Error = E>
203//! {
204//!     service.call(ctx).await
205//! }
206//! ```
207//! For more detailed middleware documentation please reference [middleware]
208//!
209//! ## Cross crates integration
210//! use tower-http inside xitca-web application.
211//! ```rust
212//! # #[cfg(feature = "tower-http-compat")]
213//! # fn _main() -> std::io::Result<()> {
214//! use tower_http::services::ServeDir;
215//! use xitca_web::{
216//!     service::tower_http_compat::TowerHttpCompat,
217//!     App
218//! };
219//!
220//! App::new()
221//!     .at("/", TowerHttpCompat::new(ServeDir::new("/some_folder")))
222//!     .serve()
223//!     .bind("localhost:8080")?
224//!     .run()
225//!     .wait()
226//! # }
227//! ```
228//!
229//! # Fast compile time
230//! - additive proc macro
231//! - light weight dependency tree
232//!
233//! ## opt-in proc macro
234//! in xitca-web proc macro is opt-in. This result in a fast compile time with zero
235//! public proc macro. That said you still can enable macros for a higher level style
236//! of API.
237//! ```rust
238//! # #[cfg(feature = "codegen")]
239//! # async fn _main() -> std::io::Result<()> {
240//! use xitca_web::{codegen::route, App};
241//!
242//! #[route("/", method = get)]
243//! async fn index() -> &'static str {
244//!     "Hello,World!"
245//! }
246//!
247//! App::new()
248//!     .at_typed(index)
249//!     .serve()
250//!     .bind("localhost:8080")?
251//!     .run()
252//!     .await
253//! # }
254//! ```
255
256#![forbid(unsafe_code)]
257#![cfg_attr(feature = "nightly", feature(async_iterator, error_generic_member_access))]
258
259mod app;
260mod context;
261#[cfg(feature = "__server")]
262mod server;
263
264pub mod body;
265pub mod error;
266pub mod handler;
267pub mod middleware;
268pub mod service;
269pub mod test;
270
271#[cfg(feature = "codegen")]
272pub mod codegen {
273    //! macro code generation module.
274
275    /// Derive macro for individual struct field type extractable through [StateRef](crate::handler::state::StateRef)
276    /// and [StateOwn](crate::handler::state::StateOwn)
277    ///
278    /// # Example:
279    /// ```rust
280    /// # use xitca_web::{codegen::State, handler::{handler_service, state::StateRef}, App, WebContext};
281    ///
282    /// // use derive macro and attribute to mark the field that can be extracted.
283    /// #[derive(State, Clone)]
284    /// struct MyState {
285    ///     #[borrow]
286    ///     field: u128
287    /// }
288    ///
289    /// # async fn app() {
290    /// // construct App with MyState type.
291    /// App::new()
292    ///     .with_state(MyState { field: 996 })
293    ///     .at("/", handler_service(index))
294    /// #   .at("/nah", handler_service(nah));
295    /// # }
296    ///
297    /// // extract u128 typed field from MyState.
298    /// async fn index(StateRef(num): StateRef<'_, u128>) -> String {
299    ///     assert_eq!(*num, 996);
300    ///     num.to_string()
301    /// }
302    /// # async fn nah(_: &WebContext<'_, MyState>) -> &'static str {
303    /// #   // needed to infer the body type of request
304    /// #   ""
305    /// # }
306    /// ```
307    pub use xitca_codegen::State;
308
309    pub use xitca_codegen::route;
310
311    pub use xitca_codegen::error_impl;
312
313    #[doc(hidden)]
314    /// a hidden module for macro to access public types that are not framework user facing.
315    pub mod __private {
316        pub use xitca_http::util::service::router::{IntoObject, RouteObject, RouterMapErr, TypedRoute};
317    }
318}
319
320pub mod http {
321    //! http types
322
323    use super::body::{RequestBody, ResponseBody};
324
325    pub use xitca_http::http::*;
326
327    /// type alias for default request type xitca-web uses.
328    pub type WebRequest<B = RequestBody> = Request<RequestExt<B>>;
329
330    /// type alias for default response type xitca-web uses.
331    pub type WebResponse<B = ResponseBody> = Response<B>;
332}
333
334pub mod route {
335    //! route services.
336    //! # Examples:
337    //! ```
338    //! # use xitca_web::{
339    //! #   handler::handler_service,
340    //! #   http::Method,
341    //! #   route::{get, post, put, Route}
342    //! # };
343    //! # fn _route() -> Result<(), Box<dyn std::error::Error>> {
344    //! // a simple async function service.
345    //! let service = handler_service(|_: ()| async { });
346    //!
347    //! let custom_method = Method::from_bytes(b"huh")?;
348    //!
349    //! // route get http method and a user custom method to handler_service
350    //! Route::new([Method::GET, custom_method]).route(service.clone());
351    //!
352    //! // shortcut for single http method route(s). they can chained multiple times.
353    //! get(service.clone()).post(service.clone()).put(service);
354    //! # Ok(())
355    //! # }
356    //! ```
357    pub use xitca_http::util::service::route::{Route, connect, delete, get, head, options, patch, post, put, trace};
358}
359
360pub use app::{App, AppObject, NestApp};
361pub use body::BodyStream;
362pub use context::WebContext;
363#[cfg(feature = "__server")]
364pub use server::HttpServer;
365
366pub use xitca_http::bytes;