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;