1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3
4#[macro_use]
5mod macros;
6
7pub mod resources;
8use resources::Resources;
9
10pub mod state;
11
12pub mod routes;
13use routes::{Catcher, ParamsNames, RawRoute, Route, Routes};
14
15#[macro_use]
16pub mod util;
17
18pub mod into;
19use into::IntoRoute;
20
21pub mod error;
22pub use error::{Error, Result};
23
24pub mod extractor;
25pub use extractor::Res;
26
27mod server;
28use server::Server;
29
30mod routing;
31use routing::{RequestConfigs, ServerShared};
32use tracing::info;
33
34#[cfg(feature = "fs")]
35#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
36pub mod fs;
37
38#[cfg(feature = "json")]
39#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
40pub mod json;
41
42#[cfg(feature = "ws")]
43#[cfg_attr(docsrs, doc(cfg(feature = "ws")))]
44pub mod ws;
45
46#[cfg(feature = "graphql")]
47#[cfg_attr(docsrs, doc(cfg(feature = "graphql")))]
48pub mod graphql;
49
50#[cfg(feature = "api")]
51#[cfg_attr(docsrs, doc(cfg(feature = "api")))]
52pub mod api;
53
54#[cfg(feature = "json")]
55#[doc(hidden)]
56pub use serde_json;
57
58pub mod service {
59 pub use crate::server::ChuchiService;
60}
61
62use std::any::Any;
63use std::net::SocketAddr;
64use std::sync::Arc;
65use std::time::Duration;
66
67use tokio::net::ToSocketAddrs;
68use tokio::task::JoinHandle;
69
70pub use chuchi_core::{
71 body, header, request, response, Body, Request, Response,
72};
73
74pub use chuchi_codegen::*;
75
76pub async fn build(addr: impl ToSocketAddrs) -> Result<Chuchi> {
78 Chuchi::new(addr).await
79}
80
81pub struct Chuchi {
83 addr: SocketAddr,
84 resources: Resources,
85 routes: Routes,
86 configs: RequestConfigs,
87}
88
89impl Chuchi {
90 pub(crate) async fn new<A>(addr: A) -> Result<Self>
91 where
92 A: ToSocketAddrs,
93 {
94 let mut me = Self::new_localhost();
95 me.update_addr(addr).await?;
96
97 Ok(me)
98 }
99
100 pub fn new_localhost() -> Self {
102 Self {
103 addr: ([127, 0, 0, 1], 0).into(),
104 resources: Resources::new(),
105 routes: Routes::new(),
106 configs: RequestConfigs::new(),
107 }
108 }
109
110 pub fn resources(&self) -> &Resources {
112 &self.resources
113 }
114
115 pub fn add_resource<R>(&mut self, resource: R)
116 where
117 R: Any + Send + Sync,
118 {
119 self.resources.insert(resource);
120 }
121
122 pub fn add_raw_route<R>(&mut self, route: R)
124 where
125 R: RawRoute + 'static,
126 {
127 let path = route.path();
128 let names = ParamsNames::parse(&path.path);
129 route.validate_requirements(&names, &self.resources);
130 self.routes.push_raw(path, route)
131 }
132
133 pub fn add_route<R>(&mut self, route: R)
135 where
136 R: IntoRoute + 'static,
137 {
138 let route = route.into_route();
139 let path = route.path();
140 let names = ParamsNames::parse(&path.path);
141 route.validate_requirements(&names, &self.resources);
142 self.routes.push(path, route)
143 }
144
145 pub fn add_catcher<C>(&mut self, catcher: C)
147 where
148 C: Catcher + 'static,
149 {
150 catcher.validate_data(&self.resources);
151 self.routes.push_catcher(catcher)
152 }
153
154 pub fn request_size_limit(&mut self, size_limit: usize) {
162 self.configs.size_limit(size_limit)
163 }
164
165 pub fn request_timeout(&mut self, timeout: Duration) {
170 self.configs.timeout(timeout)
171 }
172
173 pub async fn update_addr<A>(&mut self, addr: A) -> Result<()>
177 where
178 A: ToSocketAddrs,
179 {
180 let addr = tokio::net::lookup_host(addr)
181 .await
182 .map_err(Error::from_server_error)?
183 .next()
184 .unwrap();
185 self.addr = addr;
186 Ok(())
187 }
188
189 pub async fn build(self) -> Result<ChuchiServer> {
194 let wood = Arc::new(ServerShared::new(
195 self.resources,
196 self.routes,
197 self.configs,
198 ));
199
200 let server = Server::bind(self.addr, wood.clone()).await?;
201
202 Ok(ChuchiServer {
203 shared: wood,
204 server,
205 })
206 }
207
208 pub async fn run(self) -> Result<()> {
213 let server = self.build().await?;
214 server.run().await
215 }
216
217 pub fn run_task(self) -> JoinHandle<()> {
222 tokio::spawn(async move { self.run().await.unwrap() })
223 }
224
225 pub fn into_shared(self) -> ChuchiShared {
232 let wood = Arc::new(ServerShared::new(
233 self.resources,
234 self.routes,
235 self.configs,
236 ));
237
238 ChuchiShared { inner: wood }
239 }
240}
241
242pub struct ChuchiServer {
244 shared: Arc<ServerShared>,
245 server: Server,
246}
247
248impl ChuchiServer {
249 pub fn local_addr(&self) -> Option<SocketAddr> {
250 self.server.local_addr().ok()
251 }
252
253 pub fn shared(&self) -> ChuchiShared {
254 ChuchiShared {
255 inner: self.shared.clone(),
256 }
257 }
258
259 pub async fn run(self) -> Result<()> {
260 info!("Running server on addr: {}", self.local_addr().unwrap());
261
262 #[cfg(any(feature = "http1", feature = "http2"))]
263 {
264 self.server.serve().await
265 }
266
267 #[cfg(not(any(feature = "http1", feature = "http2")))]
268 {
269 panic!("http1 or http2 feature must be enabled")
270 }
271 }
272}
273
274#[derive(Clone)]
275pub struct ChuchiShared {
276 inner: Arc<ServerShared>,
277}
278
279impl ChuchiShared {
280 #[deprecated = "Use `ChuchiShared::resources` instead."]
281 pub fn data(&self) -> &Resources {
282 self.inner.data()
283 }
284
285 pub fn resources(&self) -> &Resources {
287 self.inner.data()
288 }
289
290 pub async fn route(&self, req: &mut Request) -> Option<Result<Response>> {
296 routing::route(&self.inner, req).await
297 }
298}