use axum::{ extract::{Host, OriginalUri, Json}, http::{header::HeaderMap, Method}, Router, }; use clap::Parser; use serde::Serialize; use serde_json::{json, Value}; use std::collections::BTreeMap; #[derive(Parser)] #[command(author, version, about, long_about = None)] struct CliArgs { /// Port to run on. #[arg(short, long, default_value_t = 8080)] port: u16, /// Listen to IP mask #[arg(short, long, default_value = "0.0.0.0")] ips: String, } #[derive(Serialize)] struct EchoHeader { name: String, value: String, } #[derive(Serialize)] struct EchoResponse { method: String, path: String, host: String, headers: BTreeMap, body: Value, } async fn echo_request( method: Method, original_uri: OriginalUri, host: Host, header_map: HeaderMap, body: Option> ) -> Json { let method = method.to_string(); let host = host.0; let path = original_uri.path().to_string(); let headers = header_map .iter() .map(|(name, value)| (name.to_string(), value.to_str().unwrap_or("").to_string())) .collect(); // TODO: Logging the method and path. let json_body = match body { None => { // TODO: Adding logging. json!({"error": "Non-JSON request sent."}) }, Some(Json(value)) => value, }; let response = EchoResponse { method, host, path, headers, body: json_body, }; Json(response) } #[tokio::main] async fn main() { let cli_args = CliArgs::parse(); let listen_on = (cli_args.ips, cli_args.port); let listen_on = format!("{ip}:{port}", ip = listen_on.0, port = listen_on.1); let app = Router::new().fallback(echo_request); let listener = tokio::net::TcpListener::bind(&listen_on) .await .unwrap_or_else(|_| panic!("Attempted binding to {}", listen_on)); axum::serve(listener, app) .await .expect("Server should start"); }