2023-06-10 22:30:36 +00:00
|
|
|
use std::{error::Error, ops::Range};
|
|
|
|
|
2023-06-15 20:13:12 +00:00
|
|
|
use unicode_segmentation::UnicodeSegmentation;
|
|
|
|
|
2023-06-10 22:30:36 +00:00
|
|
|
pub fn validate_optional_length<E: Error>(
|
|
|
|
opt: &Option<String>,
|
|
|
|
len_range: Range<usize>,
|
|
|
|
err: E,
|
|
|
|
) -> Result<Option<String>, E> {
|
|
|
|
if let Some(opt) = opt {
|
|
|
|
let opt = opt.trim();
|
2023-06-15 20:13:12 +00:00
|
|
|
let len = opt.graphemes(true).size_hint().1.unwrap();
|
|
|
|
if !len_range.contains(&len) {
|
2023-06-10 22:30:36 +00:00
|
|
|
Err(err)
|
|
|
|
} else {
|
|
|
|
Ok(Some(opt.to_string()))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
2023-05-29 00:55:16 +00:00
|
|
|
}
|
2023-06-21 23:30:13 +00:00
|
|
|
|
2023-07-02 22:16:47 +00:00
|
|
|
/// Serde deserialization decorator to map empty Strings to None
|
2023-06-21 23:30:13 +00:00
|
|
|
pub fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
|
|
|
|
where
|
|
|
|
D: serde::Deserializer<'de>,
|
|
|
|
T: std::str::FromStr,
|
|
|
|
T::Err: std::fmt::Display,
|
|
|
|
{
|
|
|
|
let opt = <Option<String> as serde::Deserialize>::deserialize(de)?;
|
|
|
|
match opt.as_deref() {
|
|
|
|
None | Some("") => Ok(None),
|
|
|
|
Some(s) => std::str::FromStr::from_str(s)
|
|
|
|
.map_err(serde::de::Error::custom)
|
|
|
|
.map(Some),
|
|
|
|
}
|
|
|
|
}
|
2023-07-02 22:16:47 +00:00
|
|
|
|
|
|
|
/// Convert a stringy number like "1999" to a 64-bit signed unix epoch-based
|
|
|
|
/// timestamp
|
|
|
|
pub fn year_to_epoch(year: Option<&str>) -> Option<i64> {
|
|
|
|
year?
|
|
|
|
.trim()
|
|
|
|
.parse::<i32>()
|
|
|
|
.map(|year| {
|
|
|
|
let years = (year - 1970) as f32;
|
|
|
|
let days = (years * 365.2425) as i64;
|
|
|
|
days * 24 * 60 * 60
|
|
|
|
})
|
|
|
|
.ok()
|
|
|
|
}
|