http-server 0.1.0

Simple and configurable command-line HTTP server
Documentation
use anyhow::{Error, Result};
use serde::Deserialize;
use std::fs;
use std::net::IpAddr;
use std::path::PathBuf;
use std::str::FromStr;

#[derive(Debug, Deserialize)]
pub struct ConfigFile {
    pub host: IpAddr,
    pub port: u16,
    pub verbose: bool,
    pub root_dir: Option<PathBuf>,
}

impl ConfigFile {
    pub fn from_file(path: Option<PathBuf>) -> Result<Self> {
        let file_path = if let Some(path) = path {
            path
        } else {
            PathBuf::from_str("server.toml").unwrap()
        };

        let file = fs::read_to_string(file_path)?;
        let config = ConfigFile::parse_toml(file.as_str())?;

        Ok(config)
    }

    fn parse_toml(content: &str) -> Result<Self> {
        match toml::from_str(content) {
            Ok(config) => Ok(config),
            Err(err) => Err(Error::msg(format!(
                "Failed to parse config from file. {}",
                err.to_string()
            ))),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::net::Ipv4Addr;

    use super::*;

    #[test]
    fn parses_config_from_file() {
        let file_contents = r#"
            host = "192.168.0.1"
            port = 7878
            verbose = true
            root_dir = "~/Desktop"
        "#;
        let host = IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1));
        let port = 7878;
        let root_dir = PathBuf::from_str("~/Desktop").unwrap();
        let config = ConfigFile::parse_toml(file_contents).unwrap();

        assert_eq!(config.host, host);
        assert_eq!(config.port, port);
        assert_eq!(config.root_dir.unwrap(), root_dir);
        assert!(config.verbose);
    }

    #[test]
    #[should_panic(
        expected = "Failed to parse config from file. missing field `host` at line 1 column 1"
    )]
    fn checks_invalid_config_from_file() {
        let file_contents = r#"
            port = 7878
        "#;
        ConfigFile::parse_toml(file_contents).unwrap();
    }
}