use std::{error::Error, ops::Range}; use unicode_segmentation::UnicodeSegmentation; pub fn validate_optional_length( opt: &Option, len_range: Range, err: E, ) -> Result, E> { if let Some(opt) = opt { let opt = opt.trim(); let len = opt.graphemes(true).size_hint().1.unwrap(); if !len_range.contains(&len) { Err(err) } else { Ok(Some(opt.to_string())) } } else { Ok(None) } } /// Serde deserialization decorator to map empty Strings to None pub fn empty_string_as_none<'de, D, T>(de: D) -> Result, D::Error> where D: serde::Deserializer<'de>, T: std::str::FromStr, T::Err: std::fmt::Display, { let opt = 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), } } /// Convert a stringy number like "1999" to a 64-bit signed unix epoch-based /// timestamp pub fn year_to_epoch(year: Option<&str>) -> Option { year? .trim() .parse::() .map(|year| { let years = (year - 1970) as f32; let days = (years * 365.2425) as i64; days * 24 * 60 * 60 }) .ok() }