use std::path::{Path, PathBuf}; use serde::Deserialize; use structopt::StructOpt; use warp::Filter; #[derive(Debug, Deserialize)] struct Config { webroot: PathBuf, port: u16, tls_key: PathBuf, tls_cert: PathBuf, } #[derive(Debug, StructOpt)] #[structopt(name = "ewww", about = "Web server for static sites")] struct Opt { #[structopt(parse(from_os_str))] config: PathBuf, } #[derive(Debug, thiserror::Error)] enum EwwwError { #[error("Web root {0} does not exist")] WebrootNotFound(PathBuf), #[error("TLS certificate {0} does not exist")] TlsCertNotFound(PathBuf), #[error("TLS key {0} does not exist")] TlsKeyNotFound(PathBuf), } #[tokio::main] async fn main() { let opt = Opt::from_args(); let config = read_config(&opt.config).unwrap(); let webroot = config.webroot.canonicalize().unwrap(); eprintln!("webroot: {:?}", webroot); let webroot = warp::any().and(warp::fs::dir(webroot)); eprintln!("starting server: {:?}", config); warp::serve(webroot) .tls() .key_path(config.tls_key) .cert_path(config.tls_cert) .run(([127, 0, 0, 1], config.port)) .await; } fn read_config(filename: &Path) -> anyhow::Result { let config = std::fs::read_to_string(filename)?; let config: Config = serde_yaml::from_str(&config)?; check_config(&config)?; Ok(config) } fn check_config(config: &Config) -> anyhow::Result<()> { if !config.webroot.exists() { return Err(EwwwError::WebrootNotFound(config.webroot.clone()).into()); } if !config.tls_cert.exists() { return Err(EwwwError::TlsCertNotFound(config.tls_cert.clone()).into()); } if !config.tls_key.exists() { return Err(EwwwError::TlsKeyNotFound(config.tls_key.clone()).into()); } Ok(()) }