use serde::Deserialize; use std::path::{Path, PathBuf}; use structopt::StructOpt; use warp::Filter; #[macro_use] extern crate log; #[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)] #[allow(clippy::enum_variant_names)] 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() { pretty_env_logger::init(); info!("ewww starting up"); let opt = Opt::from_args(); let config = read_config(&opt.config).unwrap(); let webroot = config.webroot.canonicalize().unwrap(); info!("webroot: {:?}", webroot); let log = warp::log("ewww"); let webroot = warp::any().and(warp::fs::dir(webroot)).with(log); 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 { debug!("reading config from {:?}", filename); let config = std::fs::read_to_string(filename)?; let config: Config = serde_yaml::from_str(&config)?; check_config(&config)?; info!("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(()) }