#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
mod data;
pub use data::Data;
pub mod routes;
use routes::{Catcher, RawRoute, Route, Routes};
#[macro_use]
pub mod util;
pub mod into;
use into::IntoRoute;
pub mod error;
pub use error::{Error, Result};
mod server;
use server::Server;
mod fire;
use fire::{RequestConfigs, Wood};
#[cfg(feature = "fs")]
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
pub mod fs;
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub mod json;
#[cfg(feature = "ws")]
#[cfg_attr(docsrs, doc(cfg(feature = "ws")))]
pub mod ws;
#[cfg(feature = "graphql")]
#[cfg_attr(docsrs, doc(cfg(feature = "graphql")))]
pub mod graphql;
pub mod service {
pub use crate::server::FireService;
}
use std::any::Any;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use tokio::net::ToSocketAddrs;
use tokio::task::JoinHandle;
pub use types;
pub use types::{body, header, Body, Request, Response};
pub use codegen::*;
pub async fn build(addr: impl ToSocketAddrs) -> Result<FireBuilder> {
FireBuilder::new(addr).await
}
pub struct FireBuilder {
addr: SocketAddr,
data: Data,
routes: Routes,
configs: RequestConfigs,
show_startup_msg: bool,
}
impl FireBuilder {
pub(crate) async fn new<A>(addr: A) -> Result<Self>
where
A: ToSocketAddrs,
{
let addr = tokio::net::lookup_host(addr)
.await
.map_err(Error::from_server_error)?
.next()
.unwrap();
Ok(Self {
addr,
data: Data::new(),
routes: Routes::new(),
configs: RequestConfigs::new(),
show_startup_msg: true,
})
}
pub fn data(&self) -> &Data {
&self.data
}
pub fn add_data<D>(&mut self, data: D)
where
D: Any + Send + Sync,
{
self.data.insert(data);
}
pub fn add_raw_route<R>(&mut self, route: R)
where
R: RawRoute + 'static,
{
route.validate_data(&self.data);
self.routes.push_raw(route)
}
pub fn add_route<R>(&mut self, route: R)
where
R: IntoRoute + 'static,
{
let route = route.into_route();
route.validate_data(&self.data);
self.routes.push(route)
}
pub fn add_catcher<C>(&mut self, catcher: C)
where
C: Catcher + 'static,
{
catcher.validate_data(&self.data);
self.routes.push_catcher(catcher)
}
pub fn request_size_limit(&mut self, size_limit: usize) {
self.configs.size_limit(size_limit)
}
pub fn request_timeout(&mut self, timeout: Duration) {
self.configs.timeout(timeout)
}
pub fn hide_startup_message(&mut self) {
self.show_startup_msg = false;
}
pub async fn build(self) -> Result<Fire> {
let wood = Arc::new(Wood::new(self.data, self.routes, self.configs));
let server = Server::bind(self.addr, wood.clone()).await?;
Ok(Fire {
wood,
server,
show_startup_msg: self.show_startup_msg,
})
}
pub async fn ignite(self) -> Result<()> {
let fire = self.build().await?;
fire.ignite().await
}
pub fn ignite_task(self) -> JoinHandle<()> {
tokio::spawn(async move { self.ignite().await.unwrap() })
}
pub fn into_pit(self) -> FirePit {
let wood = Arc::new(Wood::new(self.data, self.routes, self.configs));
FirePit { wood }
}
}
pub struct Fire {
wood: Arc<Wood>,
server: Server,
show_startup_msg: bool,
}
impl Fire {
pub fn local_addr(&self) -> Option<SocketAddr> {
self.server.local_addr().ok()
}
pub fn pit(&self) -> FirePit {
FirePit {
wood: self.wood.clone(),
}
}
pub async fn ignite(self) -> Result<()> {
if self.show_startup_msg {
eprintln!("Running server on addr: {}", self.local_addr().unwrap());
}
self.server.serve().await
}
}
#[derive(Clone)]
pub struct FirePit {
wood: Arc<Wood>,
}
impl FirePit {
pub fn data(&self) -> &Data {
self.wood.data()
}
pub async fn route(&self, req: &mut Request) -> Option<Result<Response>> {
eprintln!("fire pit route {req:?}");
fire::route(&self.wood, req).await
}
}