nova-forms 0.1.11

Build online forms with ease.
Documentation

use std::{cell::LazyCell, convert::Infallible, fmt::{self, Display, Debug}, marker::PhantomData, ops::Deref, str::FromStr};

use leptos::IntoAttribute;
use regex::Regex;

use super::Datatype;

pub trait Rule: 'static {
    const REGEX: LazyCell<Regex>;
    const DEFAULT: &'static str;
    const ATTRIBUTES: &'static [(&'static str, &'static str)] = &[("type", "text")];
}

pub struct Field<R: Rule> {
    value: String,
    _rule: PhantomData<R>,
}

pub enum FieldError<R: Rule> {
    InvalidFormat(PhantomData<R>),
}

impl<R: Rule> Debug for FieldError<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "FieldError({:?})", self)
    }
}

impl<R: Rule> Display for FieldError<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::InvalidFormat(_) => write!(f, "invalid format"),
        }
    }
}

impl<R: Rule> std::error::Error for FieldError<R> {}

impl<R: Rule> Clone for FieldError<R> {
    fn clone(&self) -> Self {
        match self {
            Self::InvalidFormat(_) => Self::InvalidFormat(PhantomData),
        }
    }
}

impl<R: Rule> Copy for FieldError<R> {}

impl<R: Rule> PartialEq for FieldError<R> {
    fn eq(&self, other: &Self) -> bool {
        matches!(self, Self::InvalidFormat(_)) && matches!(other, Self::InvalidFormat(_))
    }
}

impl<R: Rule> Eq for FieldError<R> {}

impl<R: Rule> From<Infallible> for FieldError<R> {
    fn from(_: Infallible) -> Self {
        unreachable!()
    }
}

impl<R: Rule> Datatype for Field<R> {
    type Inner = String;
    type Error = FieldError<R>;

    fn validate(input: String) -> Result<Field<R>, FieldError<R>> {
        if !R::REGEX.is_match(&input) {
            Err(FieldError::InvalidFormat(PhantomData))
        } else {
            Ok(Field {
                value: input,
                _rule: PhantomData,
            })
        }
    }

    fn attributes() -> Vec<(&'static str, leptos::Attribute)> {
        vec![("type", "text".into_attribute())]
    }
}

impl<R: Rule> Clone for Field<R> {
    fn clone(&self) -> Self {
        Self {
            value: self.value.clone(),
            _rule: PhantomData,
        }
    }
}

impl<R: Rule> PartialEq for Field<R> {
    fn eq(&self, other: &Self) -> bool {
        self.value == other.value
    }
}

impl<R: Rule> Eq for Field<R> {}

impl<R: Rule> Default for Field<R> {
    fn default() -> Self {
        Self::validate(R::DEFAULT.into()).unwrap()
    }
}

impl<R: Rule> Display for Field<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.value)
    }
}

impl<R: Rule> Debug for Field<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Field({:?})", self.value)
    }
}

impl<R: Rule> Deref for Field<R> {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        &self.value
    }
}

impl<R: Rule> Into<String> for Field<R> {
    fn into(self) -> String {
        self.value
    }
}

impl<R: Rule> FromStr for Field<R> {
    type Err = <Self as Datatype>::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        <Self as Datatype>::validate(<Self as Datatype>::Inner::from_str(s).map_err(<Self as Datatype>::Error::from)?)
    }
}

impl<'de, R: Rule> serde::Deserialize<'de> for Field<R> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let value = <Self as Datatype>::Inner::deserialize(deserializer)?;
        Self::validate(value).map_err(serde::de::Error::custom)
    }
}

impl<R: Rule> serde::Serialize for Field<R> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.value.serialize(serializer)
    }
}