diff --git a/src/config.rs b/src/config.rs index e7c61a68b216427c6109990dc1d9d404840bb5b1..9a900407ca2f7d1a067f5c5f7ea5c7a97bbefe3c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,10 +1,14 @@ -use serde::{Serialize, Deserialize}; -use serde::{self, Serializer, Deserializer}; -use std::net::IpAddr; use chrono::Duration; +use serde::{self, Deserialize, Deserializer}; +use serde::de::{self, Visitor}; +use std::fmt; +use std::marker::PhantomData; +use std::net::IpAddr; +use std::str::FromStr; +// use void::Void; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Deserialize)] pub struct Config { pub name: String, pub description: String, @@ -22,7 +26,7 @@ impl Config { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Deserialize)] pub struct Domain { /// the domain suffix. eg. for a dynamic domain /// mydomain.ddns.org the name here is ddns.org @@ -33,5 +37,50 @@ pub struct Domain { /// domain is allowed to updated to pub allowed_ips: Vec<String>, /// duration in days before a subdomain gets 'released` - pub validity: usize, + #[serde(deserialize_with = "deserialize_duration")] + pub validity: Duration, +} + + + +fn deserialize_duration<'de, D>(deserializer: D) -> Result<Duration, D::Error> +where + // T: Deserialize<'de> + FromStr<Err = String>, + D: Deserializer<'de>, +{ + // This is a Visitor that forwards string types to T's `FromStr` impl and + // forwards map types to T's `Deserialize` impl. The `PhantomData` is to + // keep the compiler from complaining about T being an unused generic type + // parameter. We need T in order to know the Value type for the Visitor + // impl. + struct DurationDeserializer(PhantomData<fn() -> Duration>); + + impl<'de> Visitor<'de> for DurationDeserializer + { + type Value = Duration; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string or integer") + } + + fn visit_i64<E>(self, value: i64) -> Result<Duration, E> + where E: de::Error { + Ok(Duration::hours(value)) + } + + // fn visit_i32<E>(self, value: i32) -> Result<Duration, E> + // where E: de::Error { + // Ok(Duration::hours(value as i64)) + // } + + + fn visit_str<E>(self, value: &str) -> Result<Duration, E> + where + E: de::Error, + { + Ok(Duration::hours(FromStr::from_str(value).unwrap())) + } + } + + deserializer.deserialize_any(DurationDeserializer(PhantomData)) } diff --git a/src/ffdyndns.rs b/src/ffdyndns.rs index 4ea8f2e06881df25a4bf808d4474f7f84dd9bf4c..70d0aaf6b9d97006ab78780fe28582072765194f 100644 --- a/src/ffdyndns.rs +++ b/src/ffdyndns.rs @@ -111,7 +111,7 @@ impl Service { } let token = generate_token(); - let domain = Domain::new_with_token(&d, token.clone(), Duration::days(CONFIG.get_domain_config(&d.strip_subdomain()).unwrap().validity as i64)); + let domain = Domain::new_with_token(&d, token.clone(), CONFIG.get_domain_config(&d.strip_subdomain()).unwrap().validity); self.db.insert_new_domain(&domain); Ok(token)