2024-10-14 20:21:19 +02:00
|
|
|
use core::panic;
|
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
use std::{env, fmt::Display};
|
|
|
|
|
use tokio::signal::unix::{signal, SignalKind};
|
|
|
|
|
use tokio_util::sync::CancellationToken;
|
|
|
|
|
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
|
|
|
|
use web_template::Config;
|
2024-04-21 23:02:59 +02:00
|
|
|
|
2024-10-14 20:21:19 +02:00
|
|
|
#[tokio::main]
|
|
|
|
|
async fn main() -> Result<(), String> {
|
|
|
|
|
// Enable logging in JSON format
|
2024-04-21 23:02:59 +02:00
|
|
|
tracing_subscriber::registry()
|
2024-10-14 20:21:19 +02:00
|
|
|
.with(fmt::layer().json())
|
|
|
|
|
.with(EnvFilter::from_env("LOG_LEVEL"))
|
2024-04-21 23:02:59 +02:00
|
|
|
.init();
|
|
|
|
|
|
2024-10-14 20:21:19 +02:00
|
|
|
// On panic format the message as JSON before exit
|
|
|
|
|
std::panic::set_hook(Box::new(|panic_info| {
|
|
|
|
|
let location = panic_info
|
|
|
|
|
.location()
|
|
|
|
|
.map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column()))
|
|
|
|
|
.unwrap_or_else(|| "unknown location".to_string());
|
2024-04-21 23:02:59 +02:00
|
|
|
|
2024-10-14 20:21:19 +02:00
|
|
|
let message = match panic_info.payload().downcast_ref::<String>() {
|
|
|
|
|
Some(s) => s,
|
|
|
|
|
None => "unknown message",
|
|
|
|
|
};
|
2024-04-21 23:02:59 +02:00
|
|
|
|
2024-10-14 20:21:19 +02:00
|
|
|
tracing::error!("Panic occured at {}: {}", location, message)
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Parse environment variables and create a Config struct
|
|
|
|
|
let config = Config {
|
|
|
|
|
listen_port: parse_env("LISTEN_PORT", 8080),
|
|
|
|
|
};
|
2024-04-21 23:02:59 +02:00
|
|
|
|
2024-10-14 20:21:19 +02:00
|
|
|
let (Ok(mut sigterm), Ok(mut sigint)) = (
|
|
|
|
|
signal(SignalKind::terminate()),
|
|
|
|
|
signal(SignalKind::interrupt()),
|
|
|
|
|
) else {
|
|
|
|
|
panic!("Failed to install signal handlers");
|
|
|
|
|
};
|
2024-04-21 23:02:59 +02:00
|
|
|
|
2024-10-14 20:21:19 +02:00
|
|
|
// A token to be used to signal a shutdown request
|
|
|
|
|
let token = CancellationToken::new();
|
2024-04-21 23:02:59 +02:00
|
|
|
|
2024-10-14 20:21:19 +02:00
|
|
|
let mut webserver_handle = tokio::spawn(web_template::run(config, token.clone()));
|
|
|
|
|
|
|
|
|
|
tokio::select! {
|
|
|
|
|
_ = sigterm.recv() => {
|
|
|
|
|
tracing::info!("Received SIGTERM");
|
|
|
|
|
token.cancel();
|
|
|
|
|
},
|
|
|
|
|
_ = sigint.recv() => {
|
|
|
|
|
tracing::info!("Received SIGINT");
|
|
|
|
|
token.cancel();
|
|
|
|
|
},
|
|
|
|
|
_ = &mut webserver_handle => {},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let _ = webserver_handle.await;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2024-04-21 23:02:59 +02:00
|
|
|
|
2024-10-14 20:21:19 +02:00
|
|
|
fn parse_env<T>(env_name: &str, default: T) -> T
|
|
|
|
|
where
|
|
|
|
|
T: Display + FromStr,
|
|
|
|
|
<T as FromStr>::Err: std::fmt::Display,
|
|
|
|
|
{
|
|
|
|
|
let Ok(env_value) = env::var(env_name) else {
|
|
|
|
|
tracing::info!("Environment variable '{env_name}' not set, using default {default}");
|
|
|
|
|
return default;
|
|
|
|
|
};
|
2024-04-21 23:02:59 +02:00
|
|
|
|
2024-10-14 20:21:19 +02:00
|
|
|
str::parse(&env_value).unwrap_or_else(|e| {
|
|
|
|
|
let msg = format!(
|
|
|
|
|
"Environment variable '{}' could not be converted to type {}: {}",
|
|
|
|
|
env_name,
|
|
|
|
|
std::any::type_name::<T>(),
|
|
|
|
|
e
|
|
|
|
|
);
|
2024-04-21 23:02:59 +02:00
|
|
|
|
2024-10-14 20:21:19 +02:00
|
|
|
panic!("{}", msg);
|
|
|
|
|
})
|
2024-04-21 23:02:59 +02:00
|
|
|
}
|