Rust library to generate static websites
1use axum::{body::Body, http::Uri, response::Response};
2use colored::Colorize;
3use local_ip_address::local_ip;
4use quanta::Instant;
5use std::{
6 net::{IpAddr, SocketAddr},
7 time::Duration,
8};
9use tokio::net::TcpSocket;
10use tower_http::trace::OnResponse;
11use tracing::{Span, debug, info};
12
13use crate::logging::{FormatElapsedTimeOptions, format_elapsed_time};
14
15pub fn log_server_start(start_time: Instant, host: bool, addr: SocketAddr, server_type: &str) {
16 info!(name: "SKIP_FORMAT", "");
17 let elapsed_time = format_elapsed_time(
18 start_time.elapsed(),
19 &FormatElapsedTimeOptions::default_dev(),
20 );
21 info!(name: "SKIP_FORMAT", "{} {}", "Maudit 👑".bold().bright_red(), format!("{} server started in {}", server_type, elapsed_time));
22 info!(name: "SKIP_FORMAT", "");
23
24 let port = addr.port();
25 let url = format!("\x1b]8;;http://localhost:{port}\x1b\\http://localhost:{port}\x1b]8;;\x1b\\")
26 .bold()
27 .underline()
28 .bright_blue();
29 let network_url = if host {
30 let local_ip = local_ip().unwrap();
31 format!("\x1b]8;;http://{local_ip}:{port}\x1b\\http://{local_ip}:{port}\x1b]8;;\x1b\\")
32 .bold()
33 .underline()
34 .bright_magenta()
35 } else {
36 "Use --host to expose the server to your network".dimmed()
37 };
38 info!(name: "SKIP_FORMAT", "🮔 {} {}", "Local".bold(), url);
39 info!(name: "SKIP_FORMAT", "🮔 {} {}", "Network".bold(), network_url);
40 info!(name: "SKIP_FORMAT", "");
41
42 info!(name: "server", "{}", "waiting for requests...".dimmed());
43}
44
45#[derive(Clone, Debug)]
46pub struct CustomOnResponse;
47
48impl OnResponse<Body> for CustomOnResponse {
49 fn on_response(self, response: &Response<Body>, latency: Duration, _span: &Span) {
50 let status = response.status();
51
52 // Skip informational responses
53 if status.is_informational() {
54 return;
55 }
56
57 let status = if status.is_server_error() {
58 status.to_string().red()
59 } else if status.is_client_error() {
60 status.to_string().yellow()
61 } else {
62 status.to_string().green()
63 };
64
65 // There's allegedly a way to get the request URI from the span, but I can't figure it out
66 let uri = response
67 .extensions()
68 .get::<Uri>()
69 .unwrap_or(&Uri::default())
70 .to_string()
71 .bold();
72
73 let latency = format_elapsed_time(latency, &FormatElapsedTimeOptions::default());
74
75 let message = format!("{} {} {}", status, uri, latency);
76
77 info!(name: "", "{}", message);
78 }
79}
80
81pub async fn find_open_port(address: &IpAddr, starting_port: u16) -> u16 {
82 let mut port = starting_port;
83
84 loop {
85 let socket = TcpSocket::new_v4().unwrap();
86 let socket_addr = SocketAddr::new(*address, port);
87 match socket.bind(socket_addr) {
88 Ok(_) => {
89 debug!("Found open port: {}", port);
90 return port;
91 }
92 Err(_) => {
93 debug!(
94 "Port {} is already in use or failed to bind, trying next one",
95 port
96 );
97 port += 1;
98 }
99 }
100 }
101}