Improve error messages when parsing config

This commit is contained in:
2023-10-05 18:37:57 +02:00
parent ba7c2d4b71
commit 8ccf64c28b

View File

@ -1,6 +1,6 @@
use chrono::Duration; use chrono::Duration;
use serde::de::Visitor; use serde::de::Visitor;
use serde::{Deserialize, Deserializer}; use serde::{de::Error, Deserialize, Deserializer};
use std::fmt; use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
@ -56,17 +56,34 @@ impl ConfPeriod {
} }
} }
/// Deserialize a [SimpleDuration] from a string like "3d" or "24h".
fn parse_simple_duration<'de, D>(d: D) -> Result<SimpleDuration, D::Error> fn parse_simple_duration<'de, D>(d: D) -> Result<SimpleDuration, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let s = d.deserialize_string(StringVisitor)?; let s = d.deserialize_str(StrVisitor)?;
let s = s.trim(); let s = s.trim();
let suffix = s.chars().rev().next().unwrap(); if s.contains(char::is_whitespace) {
return Err(D::Error::custom("duration can't include whitespace"));
}
let suffix = s
.chars()
.next_back()
.ok_or_else(|| D::Error::custom("duration can't be empty"))?;
if suffix.is_ascii_digit() {
return Err(D::Error::custom(
r#"specify duration with a suffix, i.e. "24h""#,
));
}
let value = &s[..s.len() - suffix.len_utf8()]; let value = &s[..s.len() - suffix.len_utf8()];
let value: u64 = value.parse().expect("failed to parse duration value"); let value: u64 = value
.parse()
.map_err(|e| D::Error::custom(format!("failed to parse duration value: {e}")))?;
let value = value as i64; let value = value as i64;
use SimpleDuration::*; use SimpleDuration::*;
@ -76,24 +93,23 @@ where
'h' => Hours(value), 'h' => Hours(value),
'd' => Days(value), 'd' => Days(value),
'w' => Weeks(value), 'w' => Weeks(value),
_ => panic!("unknown unit of duration"), d => return Err(D::Error::custom(format!("unknown unit of duration: {d:?}"))),
}) })
} }
struct StringVisitor; struct StrVisitor;
impl<'de> Visitor<'de> for StringVisitor { impl<'de> Visitor<'de> for StrVisitor {
type Value = String; type Value = &'de str;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string") formatter.write_str("a string")
} }
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> { fn visit_borrowed_str<E>(self, s: &'de str) -> Result<Self::Value, E>
Ok(value) where
} E: Error,
{
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> { Ok(s)
Ok(value.to_string())
} }
} }