use phf::phf_set;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
};
use unicase::UniCase;
#[cfg_attr(nightly, doc(cfg(feature = "from_form")))]
pub trait FromForm: Sized {
fn from_form<'f, I, K, V>(form: I) -> Result<Self, FromFormError>
where
I: Iterator<Item = (K, V)>,
K: AsRef<str> + 'f,
V: AsRef<str> + 'f;
}
impl<V, S: std::hash::BuildHasher + Default> FromForm for std::collections::HashMap<String, V, S>
where
V: for<'f> FromFormMultiple<'f>,
for<'f> <V as FromFormMultiple<'f>>::Item: FromFormValue<'f>,
for<'f> <<V as FromFormMultiple<'f>>::Item as FromFormValue<'f>>::Error: Into<anyhow::Error>,
{
fn from_form<'f, I, K, VV>(form: I) -> Result<Self, FromFormError>
where
I: Iterator<Item = (K, VV)>,
K: AsRef<str> + 'f,
VV: AsRef<str> + 'f,
{
let mut map = <std::collections::HashMap<String, V, S> as Default>::default();
for (key, value) in form {
let key = key.as_ref().to_string();
let value = V::Item::from_form_value(value.as_ref()).map_err(|e| {
FromFormError::InvalidFormat("-", std::any::type_name::<V::Item>(), e.into())
})?;
map.entry(key).or_insert_with(V::default).push(value);
}
Ok(map)
}
}
#[cfg_attr(nightly, doc(cfg(feature = "from_form")))]
pub trait FromFormValue<'f>: Sized {
type Error;
fn from_form_value(value: &'f str) -> Result<Self, Self::Error>;
}
#[cfg_attr(nightly, doc(cfg(feature = "from_form")))]
pub trait FromFormMultiple<'f>: Sized {
type Item;
fn push(&mut self, item: Self::Item);
fn default() -> Self;
}
impl<'f, V, T> FromFormMultiple<'f> for T
where
T: Default + Extend<V> + IntoIterator<Item = V>,
{
type Item = V;
fn push(&mut self, item: Self::Item) {
self.extend(Some(item));
}
fn default() -> Self {
Self::default()
}
}
impl<'f> FromFormValue<'f> for String {
type Error = std::convert::Infallible;
fn from_form_value(value: &'f str) -> Result<Self, Self::Error> {
Ok(value.to_string())
}
}
impl<'f> FromFormValue<'f> for &'f str {
type Error = std::convert::Infallible;
fn from_form_value(value: &'f str) -> Result<Self, Self::Error> {
Ok(value)
}
}
impl<'f, T> FromFormValue<'f> for Option<T>
where
T: FromFormValue<'f>,
{
type Error = std::convert::Infallible;
fn from_form_value(value: &'f str) -> Result<Self, Self::Error> {
Ok(T::from_form_value(value).ok())
}
}
impl<'f, T> FromFormValue<'f> for Result<T, T::Error>
where
T: FromFormValue<'f>,
{
type Error = std::convert::Infallible;
fn from_form_value(value: &'f str) -> Result<Self, Self::Error> {
Ok(T::from_form_value(value))
}
}
static TRUE_VALUES: phf::Set<UniCase<&'static str>> = phf_set!(
UniCase::ascii("true"),
UniCase::ascii("on"),
UniCase::ascii("yes"),
UniCase::ascii("1"),
);
impl<'f> FromFormValue<'f> for bool {
type Error = std::convert::Infallible;
fn from_form_value(value: &'f str) -> Result<Self, Self::Error> {
Ok(TRUE_VALUES.contains(&UniCase::new(value)))
}
}
macro_rules! forward_to_parse {
($($t:ty),*) => {
$(
impl<'f> FromFormValue<'f> for $t {
type Error = <$t as std::str::FromStr>::Err;
fn from_form_value(value: &'f str) -> Result<Self, Self::Error> {
value.parse()
}
}
)*
};
}
forward_to_parse! {
f32, f64, isize, i8, i16, i32, i64, i128, usize, u8, u16, u32, u64, u128,
IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr,
NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize,
NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[cfg_attr(nightly, doc(cfg(feature = "from_form")))]
pub enum FromFormError {
#[error("missing field `{0}`")]
MissingField(&'static str),
#[error("could not parse the field `{0}' as `{1}': {2}")]
InvalidFormat(&'static str, &'static str, #[source] anyhow::Error),
}