use headers::HeaderMap;
use serde::Deserialize;
use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
use crate::{helpers, Context, Result};
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum LogLevel {
Error,
Warn,
Info,
Debug,
Trace,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Headers {
pub source: String,
#[serde(with = "http_serde::header_map")]
pub headers: HeaderMap,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Manifest {
pub host: Option<String>,
pub port: Option<u8>,
pub root: Option<String>,
pub log_level: Option<LogLevel>,
pub cache_control_headers: bool,
pub compression: bool,
pub page404: Option<String>,
pub page50x: Option<String>,
pub http2: bool,
pub http2_tls_cert: PathBuf,
pub http2_tls_key: PathBuf,
pub security_headers: bool,
pub cors_allow_origins: String,
pub cors_allow_headers: Option<String>,
pub directory_listing: bool,
pub directory_listing_order: Option<u8>,
pub basic_auth: Option<String>,
pub fd: Option<usize>,
pub threads_multiplier: usize,
pub grace_period: u8,
pub page_fallback: String,
#[serde(rename(deserialize = "headers"))]
pub headers: Option<Vec<Headers>>,
}
fn read_file(path: &Path) -> Result<toml::Value> {
let toml_str = helpers::read_file(path).with_context(|| {
format!(
"error trying to deserialize configuration \"{}\" file toml.",
path.display()
)
})?;
let first_error = match toml_str.parse() {
Ok(res) => return Ok(res),
Err(err) => err,
};
let mut second_parser = toml::de::Deserializer::new(&toml_str);
second_parser.set_require_newline_after_table(false);
if let Ok(res) = toml::Value::deserialize(&mut second_parser) {
let msg = format!(
"\
TOML file found which contains invalid syntax and will soon not parse
at `{}`.
The TOML spec requires newlines after table definitions (e.g., `[a] b = 1` is
invalid), but this file has a table header which does not have a newline after
it. A newline needs to be added and this warning will soon become a hard error
in the future.",
path.display()
);
println!("{}", &msg);
return Ok(res);
}
let first_error = anyhow::Error::from(first_error);
Err(first_error.context("could not parse input as TOML format"))
}
pub fn read_manifest(config_file: &Path) -> Result<Option<Manifest>> {
let ext = config_file.extension();
if ext.is_none() || ext.unwrap().is_empty() || ext.unwrap().ne("toml") {
return Ok(None);
}
let toml = read_file(config_file).with_context(|| "error reading configuration toml file.")?;
let mut unused = BTreeSet::new();
let manifest: Manifest = serde_ignored::deserialize(toml, |path| {
let mut key = String::new();
helpers::stringify(&mut key, &path);
unused.insert(key);
})
.with_context(|| "error during configuration toml file deserialization.")?;
for key in unused {
println!(
"Warning: unused configuration manifest key \"{}\" or unsuported.",
key
);
}
Ok(Some(manifest))
}