//! Server config use super::*; use std::{ net::SocketAddr, path::Path, io, borrow::Cow, num::NonZeroU64, }; use tokio::{ fs::OpenOptions, prelude::*, time::Duration, io::BufReader, }; pub const DEFAULT_FILE_LOCATION: &'static str = "markov.toml"; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash, Serialize, Deserialize)] pub struct Config { pub bindpoint: String, pub file: String, pub max_content_length: u64, pub max_gen_size: usize, pub save_interval_secs: Option, pub trust_x_forwarded_for: bool, #[serde(default)] pub filter: FilterConfig, } #[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Hash, Serialize, Deserialize)] pub struct FilterConfig { inbound: String, #[serde(default)] outbound: String, } impl FilterConfig { pub fn get_inbound_filter(&self) -> sanitise::filter::Filter { let filt: sanitise::filter::Filter = self.inbound.parse().unwrap(); if !filt.is_empty() { info!("Loaded inbound filter: {:?}", filt.iter().collect::()); } filt } pub fn get_outbound_filter(&self) -> sanitise::filter::Filter { let filt: sanitise::filter::Filter = self.outbound.parse().unwrap(); if !filt.is_empty() { info!("Loaded outbound filter: {:?}", filt.iter().collect::()); } filt } } impl Default for Config { #[inline] fn default() -> Self { Self { bindpoint: SocketAddr::from(([127,0,0,1], 8001)).to_string(), file: "chain.dat".to_owned(), max_content_length: 1024 * 1024 * 4, max_gen_size: 256, save_interval_secs: Some(unsafe{NonZeroU64::new_unchecked(2)}), trust_x_forwarded_for: false, filter: Default::default(), } } } impl Config { pub fn save_interval(&self) -> Option { self.save_interval_secs.map(|x| Duration::from_secs(x.into())) } pub async fn load(from: impl AsRef) -> io::Result { let file = OpenOptions::new() .read(true) .open(from).await?; let mut buffer= String::new(); let reader = BufReader::new(file); let mut lines = reader.lines(); while let Some(line) = lines.next_line().await? { buffer.push_str(&line[..]); buffer.push('\n'); } toml::de::from_str(&buffer[..]).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e)) } pub async fn save(&self, to: impl AsRef) -> io::Result<()> { let config = toml::ser::to_string_pretty(self).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let mut file = OpenOptions::new() .write(true) .create(true) .truncate(true) .open(to).await?; file.write_all(config.as_bytes()).await?; file.shutdown().await?; Ok(()) } } /// Try to load config file specified by args, or default config file pub fn load() -> impl futures::future::Future> { load_args(std::env::args().skip(1)) } async fn load_args>(mut from: I) -> Option { let place = if let Some(arg) = from.next() { trace!("File {:?} provided", arg); Cow::Owned(arg) } else { warn!("No config file provided. Using default location {:?}", DEFAULT_FILE_LOCATION); Cow::Borrowed(DEFAULT_FILE_LOCATION) }; match Config::load(place.as_ref()).await { Ok(cfg) => { info!("Loaded config file {:?}", place); Some(cfg) }, Err(err) => { error!("Failed to load config file from {:?}: {}", place, err); None }, } }