use chrono_tz::Tz; use config::{Config, ConfigError, File}; use serde::de::{self, Error, Visitor}; use serde::{Deserialize, Deserializer}; use serenity::prelude::TypeMapKey; use std::fmt; use std::path::PathBuf; use std::sync::Arc; #[derive(Debug, Deserialize, Clone)] pub struct EventReminder { pub msg: String, pub reminder_time: u32, } #[derive(Debug, Deserialize, Clone)] pub struct HypeBotConfig { pub db_location: PathBuf, pub default_thumbnail_link: String, pub discord_key: String, pub prefix: String, pub event_channel: u64, pub event_roles: Vec, pub ping_roles: Vec, #[serde(deserialize_with = "from_tz_string")] pub event_timezone: Tz, pub log_path: String, pub reminders: Option>, } struct ConfigValueVisitor; impl<'de> Visitor<'de> for ConfigValueVisitor { type Value = String; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "Unable to parse timezone field.") } fn visit_str(self, s: &str) -> Result where E: de::Error, { Ok(s.to_string()) } } fn from_tz_string<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { let string = deserializer.deserialize_struct("Value", &["into_str"], ConfigValueVisitor)?; let tz: Tz = string.parse().ok().ok_or_else(|| { D::Error::custom("Unable to parse datetime, should be in format \"Country/City\"") })?; Ok(tz) } impl HypeBotConfig { pub fn new(config_path: &str) -> Result { let mut cfg = Config::new(); cfg.merge(File::with_name(config_path))?; cfg.try_into() } } impl TypeMapKey for HypeBotConfig { type Value = Arc; }