Improve error messages when parsing config
This commit is contained in:
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user