mod error;
pub mod settings;
use std::net;
use console::style;
pub use self::error::Error;
pub const PORT_PLAINTEXT: u16 = 80;
pub const PORT_ENCRYPT: u16 = 443;
pub struct Server<UserState> {
state: crate::state::State<UserState>,
pub application_settings: crate::application::Settings,
pub(crate) settings: settings::Settings,
global_router: axum::Router<()>,
pub(crate) router: axum::Router<crate::state::State<UserState>>,
pub(crate) routes: crate::routing::RouteCollection<UserState>,
static_resources: Vec<settings::SettingsStaticResource>,
}
impl<US> Server<US>
where
US: 'static,
US: crate::state::StateInterface,
{
pub fn new(
application_settings: crate::application::Settings,
) -> Result<Self, Error> {
let state = crate::state::State { user_state: None };
let settings = settings::Settings::fetch_or_default(
&application_settings.config_dir,
&application_settings.loader_extension,
);
let static_resources = settings
.static_resources
.iter()
.cloned()
.map(|mut sr| {
sr.dir_path = if sr.dir_path.is_relative() {
application_settings.root_dir.join(&sr.dir_path)
} else {
sr.dir_path.to_owned()
};
sr
})
.collect();
Ok(Self {
application_settings,
state,
settings,
global_router: axum::Router::new(),
router: axum::Router::new(),
routes: crate::routing::RouteCollection::new(),
static_resources,
})
}
fn define_routes(
mut self,
routes: crate::routing::RouteCollection<US>,
) -> Self {
let mut scoped_router = axum::Router::<crate::state::State<US>>::new();
for route in routes.all() {
scoped_router =
scoped_router.route(&route.fullpath, route.action.to_owned());
}
self.router = self.router.merge(scoped_router);
self.routes.extend(routes);
self
}
pub fn make_application<A>(self) -> Self
where
A: crate::Application<State = US>,
{
let mut this = self.define_routes(
<A::Router as crate::routing::interface::RouterExt>::routes(),
);
this.router = A::register_extension(&this.state, this.router);
this.router = A::register_layer(&this.state, this.router);
this.router = A::register_middleware(&this.state, this.router);
A::register_service(this)
}
pub async fn run(mut self) -> Result<(), Error>
where
US: 'static,
{
log::info!("Tentative de lancement du serveur web...");
println!();
println!("Liste des routes du serveur web:");
for route in self.routes.all() {
let methods = route
.methods
.iter()
.map(|m| style(&m).yellow().to_string())
.collect::<Vec<String>>()
.join(" | ");
println!(
"\t[{}]: {} {}",
methods,
style(&route.fullpath).bright().green(),
if let Some(name) = route.name.as_deref() {
format!("as {}", style(name).bright().black())
} else {
String::new()
}
);
}
println!();
for static_resource in self.static_resources.iter() {
println!(
"\t[{}]: {} -> {}",
style("STATIC RESOURCE").magenta(),
style(&static_resource.url_path).bright().green(),
style(static_resource.dir_path.display()).bright().black(),
);
self.global_router = self.global_router.nest_service(
&static_resource.url_path,
tower_http::services::ServeDir::new(&static_resource.dir_path),
);
}
self.global_router =
self.global_router.merge(self.router.with_state(self.state));
let protocol = if self.settings.tls.is_some() {
"https"
} else {
"http"
};
let host = self.settings.host;
let port = self.settings.port;
let server_socket_addr =
<_ as Into<net::SocketAddr>>::into((host, port));
let port_with_prefix = if port == PORT_PLAINTEXT || port == PORT_ENCRYPT
{
String::new()
} else {
format!(":{}", port)
};
let url = format!("{protocol}://{host}{port_with_prefix}");
type S = net::SocketAddr;
println!("URL: {url}");
if let Some(settings_tls) = self.settings.tls {
let tls_config =
axum_server::tls_rustls::RustlsConfig::from_pem_file(
&settings_tls.cert,
&settings_tls.key,
)
.await?;
let http_config =
axum_server::HttpConfig::new().http2_only(true).build();
axum_server::bind_rustls(server_socket_addr, tls_config)
.http_config(http_config)
.serve(
self.global_router
.into_make_service_with_connect_info::<S>(),
)
.await?;
} else {
axum::Server::bind(&server_socket_addr)
.serve(
self.global_router
.into_make_service_with_connect_info::<S>(),
)
.await?;
}
Ok(())
}
pub fn with_user_state(mut self, data: US::UserData) -> Self {
self.state.user_state.replace(US::new(data));
self
}
}