use serde::de::{Deserializer, Visitor, Error};
use std::ascii::AsciiExt;
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
#[serde(deserialize_with = "deserialize_limit")]
pub limit: u64,
}
fn deserialize_limit<D>(d: &mut D) -> Result<u64, D::Error>
where D: Deserializer
{
struct V;
impl Visitor for V {
type Value = u64;
fn visit_u64<E>(&mut self, v: u64) -> Result<u64, E>
where E: Error
{
Ok(v)
}
fn visit_i64<E>(&mut self, v: i64) -> Result<u64, E>
where E: Error
{
if v < 0 {
return Err(E::invalid_value("must be non-negative"));
}
Ok(v as u64)
}
fn visit_str<E>(&mut self, v: &str) -> Result<u64, E>
where E: Error
{
let (number, unit) = match v.find(|c: char| !c.is_digit(10)) {
Some(n) => (v[..n].trim(), Some(v[n..].trim())),
None => (v.trim(), None),
};
let number = match number.parse::<u64>() {
Ok(n) => n,
Err(e) => return Err(E::invalid_value(&e.to_string())),
};
let unit = match unit {
Some(u) => u,
None => return Ok(number),
};
let number = if unit.eq_ignore_ascii_case("b") {
Some(number)
} else if unit.eq_ignore_ascii_case("kb") || unit.eq_ignore_ascii_case("kib") {
number.checked_mul(1024)
} else if unit.eq_ignore_ascii_case("mb") || unit.eq_ignore_ascii_case("mib") {
number.checked_mul(1024 * 1024)
} else if unit.eq_ignore_ascii_case("gb") || unit.eq_ignore_ascii_case("gib") {
number.checked_mul(1024 * 1024 * 1024)
} else if unit.eq_ignore_ascii_case("tb") || unit.eq_ignore_ascii_case("tib") {
number.checked_mul(1024 * 1024 * 1024 * 1024)
} else {
return Err(E::invalid_value(&format!("invalid unit `{}`", unit)));
};
match number {
Some(n) => Ok(n),
None => Err(E::invalid_value("value overflowed")),
}
}
}
d.deserialize(V)
}