utils
: Create and use crate #9
5 changed files with 129 additions and 11 deletions
|
@ -15,6 +15,8 @@ axum-extra = { version = "0.9.4", features = ["query"] }
|
|||
log = { version = "0.4.22", features = ["std", "max_level_trace", "release_max_level_debug"] }
|
||||
pretty_env_logger = "0.5.0"
|
||||
mediatype = { version = "0.19.18", features = ["serde"] }
|
||||
minijinja = "2.5.0"
|
||||
tokio = { version = "1.41.1", features = ["net"] }
|
||||
|
||||
[lints.clippy]
|
||||
tabs-in-doc-comments = "allow"
|
||||
|
|
6
acrate_utils/src/ext.rs
Normal file
6
acrate_utils/src/ext.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use std::sync::Arc;
|
||||
use axum::Extension;
|
||||
|
||||
pub use minijinja;
|
||||
|
||||
pub type ExtMj = Extension<Arc<minijinja::Environment<'static>>>;
|
49
acrate_utils/src/init.rs
Normal file
49
acrate_utils/src/init.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use axum::Extension;
|
||||
|
||||
/// Initialize logging with [`pretty_env_logger::init`].
|
||||
pub fn init_logging() {
|
||||
log::trace!("Initializing logging...");
|
||||
pretty_env_logger::init();
|
||||
log::trace!("Initialized logging!");
|
||||
}
|
||||
|
||||
/// Initialize a [`tokio::net::TcpListener`] bound to the given [`SocketAddr`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If unable to bind to the given [`SocketAddr`].
|
||||
///
|
||||
pub async fn init_listener(bind: SocketAddr) -> tokio::net::TcpListener {
|
||||
log::trace!("Creating Tokio listener bound to: {bind:#?}");
|
||||
let listener = tokio::net::TcpListener::bind(bind)
|
||||
.await
|
||||
.unwrap_or_else(|_| panic!("Failed to bind to: {bind:#?}"));
|
||||
log::trace!("Created Tokio listener: {listener:#?}");
|
||||
|
||||
listener
|
||||
}
|
||||
|
||||
/// Initialize a static [`minijinja::Environment`].
|
||||
pub fn init_minijinja() -> minijinja::Environment<'static> {
|
||||
log::trace!("Creating Minijinja environment...");
|
||||
let mj = minijinja::Environment::<'static>::new();
|
||||
log::trace!("Created Minijinja environment: {mj:#?}");
|
||||
|
||||
mj
|
||||
}
|
||||
|
||||
/// Initialize an [`axum::Router`] with the given [`minijinja::Environment`] available.
|
||||
pub fn init_router(mj: minijinja::Environment<'static>) -> axum::Router {
|
||||
log::trace!("Creating Arc for the minijinja Environment...");
|
||||
let mj = Arc::new(mj);
|
||||
log::trace!("Created Arc for the minijinja Environment: {mj:#?}");
|
||||
|
||||
log::trace!("Creating Axum router...");
|
||||
let router = axum::Router::new()
|
||||
.layer(Extension(mj));
|
||||
log::trace!("Created Axum router: {router:#?}");
|
||||
|
||||
router
|
||||
}
|
|
@ -1,14 +1,50 @@
|
|||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
pub mod ext;
|
||||
pub mod init;
|
||||
pub mod run;
|
||||
|
||||
|
||||
/// Add the template file at the given path to the given [`minijinja::Environment`].
|
||||
#[macro_export]
|
||||
macro_rules! add_minijinja_template {
|
||||
($mj:ident, $path:literal) => {
|
||||
log::trace!("Adding template to minijinja Environment: {:?} ← {:#?}", $mj, $path);
|
||||
$mj.add_template($path, include_str!($path))
|
||||
.expect(concat!("Invalid Minijinja template: ", $path));
|
||||
log::trace!("Added template to minijinja Environment: {:?} ← {:#?}", $mj, $path);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
/// Add the given route to the [`axum::Router`].
|
||||
#[macro_export]
|
||||
macro_rules! add_axum_route {
|
||||
($router:ident, $path:literal, $route:expr) => {
|
||||
log::trace!("Adding route to axum Router: {:?} ← {:#?}", $router, $path);
|
||||
let $router = $router.route($path, $route);
|
||||
log::trace!("Added route to axum Router: {:?} ← {:#?}", $router, $path);
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
#[macro_export]
|
||||
macro_rules! web_server {
|
||||
(
|
||||
on: $socket_addr:expr,
|
||||
templates: [
|
||||
$( $template_path:literal ),+
|
||||
],
|
||||
routes: {
|
||||
$( $path:literal => $route:expr ),+
|
||||
}
|
||||
) => {
|
||||
$crate::init::init_logging();
|
||||
let listener = $crate::init::init_listener($socket_addr).await;
|
||||
let mut mj = $crate::init::init_minijinja();
|
||||
$(
|
||||
$crate::add_minijinja_template!(mj, $template_path);
|
||||
)+
|
||||
let router = $crate::init::init_router(mj);
|
||||
$(
|
||||
$crate::add_axum_route!(router, $path, $route);
|
||||
)+
|
||||
$crate::run::run_server(listener, router).await
|
||||
};
|
||||
}
|
||||
|
|
25
acrate_utils/src/run.rs
Normal file
25
acrate_utils/src/run.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
/// Run a web server with [`axum::serve`], using the given [`tokio::net::TcpListener`] and [`axum::Router`].
|
||||
///
|
||||
/// # Exits
|
||||
///
|
||||
/// Once the server is terminated, the process will exit with either:
|
||||
///
|
||||
/// - `0`, if [`axum::serve`] returned [`Ok`];
|
||||
/// - `1`, if [`axum::serve`] returned [`Err`].
|
||||
///
|
||||
pub async fn run_server(listener: tokio::net::TcpListener, application: axum::Router) -> std::convert::Infallible {
|
||||
log::trace!("Serving application: {listener:#?} → {application:#?}");
|
||||
let result = axum::serve(listener, application)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
log::error!("Server exited gracefully.");
|
||||
std::process::exit(0);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Server exited with an error: {e:#?}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue