use std::{ net::{IpAddr, SocketAddr}, num::NonZeroUsize, path::PathBuf, sync::Arc, thread::available_parallelism, }; use actix_web::{App, HttpServer}; use clap::{Parser, crate_name, crate_version}; #[derive(Parser, Debug)] #[clap(name = crate_name!(), version = crate_version!())] struct Args { /// Directory to expose #[clap(default_value = ".", value_parser = parse_valid_dir)] dir: PathBuf, /// IP address to use #[clap(default_value = "0.0.0.0")] ip: IpAddr, /// Port number to connect #[clap(default_value = "8080")] port: u16, } fn parse_valid_dir(dir: &str) -> Result { let path = PathBuf::from(dir); match std::fs::metadata(&path) { Ok(metadata) if metadata.is_dir() => Ok(path), Ok(_) => Err(format!("{} is not a directory", path.display())), Err(e) => Err(format!("Error accessing {}: {}", path.display(), e)), } } #[actix_web::main] async fn main() -> std::io::Result<()> { let args = Args::parse(); let dir = Arc::new(args.dir); let sock = SocketAddr::new(args.ip, args.port); println!( "Starting HTTP server on {sock} exposing dir {}", dir.display() ); HttpServer::new(move || { let dir = dir.clone(); App::new().service( actix_files::Files::new("/", dir.as_ref()) .show_files_listing() .prefer_utf8(true), ) }) .workers(available_parallelism().map_or(1, NonZeroUsize::get)) .bind(sock)? .run() .await }