use std::fmt;
pub const MAX_VALUE_LEN: usize = 100;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Problem {
Missing,
TooLong,
Reserved,
DateTime,
Date,
ShortString,
UInt,
TooLarge,
UDecimal,
Passed,
TooFar,
Unknown,
Repeated,
}
impl Problem {
#[must_use]
pub const fn words(self) -> &'static str {
match self {
Problem::Missing => "needs a value",
Problem::TooLong => "takes at most 100 characters",
Problem::Reserved => "cannot hold {, } or |",
Problem::DateTime => "takes YYYYMMDD-HHMM in UTC",
Problem::Date => "takes YYYYMMDD in UTC",
Problem::ShortString => "takes at most 10 characters",
Problem::UInt => "takes a whole number with no sign or leading zeros",
Problem::TooLarge => "takes a whole number that fits in 64 bits",
Problem::UDecimal => "takes a decimal with no sign, no exponent and no leading or trailing zeros",
Problem::Passed => "has already passed",
Problem::TooFar => "is more than a year away",
Problem::Unknown => "is not a keyword of this template",
Problem::Repeated => "is given twice",
}
}
}
impl fmt::Display for Problem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.words())
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("{keyword} {problem}")]
pub struct BadValue {
pub keyword: String,
pub problem: Problem,
}
impl BadValue {
pub(super) fn new(keyword: &str, problem: Problem) -> Self {
BadValue { keyword: keyword.to_owned(), problem }
}
}
pub(super) fn plain(keyword: &str, value: &str) -> Result<(), BadValue> {
let problem = if value.is_empty() {
Problem::Missing
} else if value.len() > MAX_VALUE_LEN {
Problem::TooLong
} else if value.contains(['{', '}', '|']) {
Problem::Reserved
} else {
return Ok(());
};
Err(BadValue::new(keyword, problem))
}