nova_forms/
datatypes.rs

1mod email;
2mod non_empty_string;
3mod phone;
4mod date_time;
5mod date;
6mod time;
7mod accept;
8mod iban;
9mod postal_code;
10mod field;
11mod regional_field;
12
13pub use email::*;
14pub use non_empty_string::*;
15pub use phone::*;
16pub use date_time::*;
17pub use date::*;
18pub use time::*;
19pub use accept::*;
20pub use iban::*;
21pub use postal_code::*;
22pub use field::*;
23pub use regional_field::*;
24
25use num_bigint::BigInt;
26use num_rational::BigRational;
27
28use leptos::*;
29use std::error::Error;
30use std::fmt::{Debug, Display};
31use std::rc::Rc;
32use std::str::FromStr;
33
34/// A datatype that represents an optional value.
35#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
36pub struct Optional<T>(Option<T>);
37
38impl<T> FromStr for Optional<T>
39where
40    T: FromStr,
41{
42    type Err = <T as FromStr>::Err;
43
44    fn from_str(s: &str) -> Result<Self, Self::Err> {
45        if s.is_empty() {
46            return Ok(Optional(None));
47        }
48        Ok(Optional(T::from_str(s).ok()))
49    }
50}
51
52impl<T> Display for Optional<T>
53where
54    T: Display,
55{
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        if let Some(value) = &self.0 {
58            write!(f, "{}", value)
59        } else {
60            write!(f, "")
61        }
62    }
63}
64
65macro_rules! impl_datatypes {
66    ( $( $t:ty where $($name:literal $( : $val:literal)? ),* $(,)? );* $(;)? ) => {
67        $(
68            impl Datatype for $t {
69                type Inner = $t;
70                type Error = <$t as FromStr>::Err;
71
72                fn validate(input: $t) -> Result<Self, <$t as FromStr>::Err> {
73                    Ok(input)
74                }
75
76                fn attributes() -> Vec<(&'static str, Attribute)> {
77                    vec![ $( ($name, {
78                        #[allow(unused)]
79                        let mut v = Attribute::Bool(true);
80                        $( v = Attribute::String(Oco::Borrowed($val)); )?
81                        v
82                    }) ),* ]
83                }
84            }
85        )*
86    };
87}
88
89impl_datatypes! {
90    u8 where "type": "number", "step": "1", "min": "0", "max": "255", "required";
91    Optional<u8> where "type": "number", "step": "1", "min": "0", "max": "255";
92    u16 where "type": "number", "step": "1", "min": "0", "max": "65535", "required";
93    Optional<u16> where "type": "number", "step": "1", "min": "0", "max": "65535";
94    u32 where "type": "number", "step": "1", "min": "0", "max": "4294967295", "required";
95    Optional<u32> where "type": "number", "step": "1", "min": "0", "max": "4294967295";
96    u64 where "type": "number", "step": "1", "min": "0", "max": "18446744073709551615", "required";
97    Optional<u64> where "type": "number", "step": "1", "min": "0", "max": "18446744073709551615";
98    u128 where "type": "number", "step": "1", "min": "0", "max": "340282366920938463463374607431768211455", "required";
99    Optional<u128> where "type": "number", "step": "1", "min": "0", "max": "340282366920938463463374607431768211455";
100    i8 where "type": "number", "step": "1", "min": "-128", "max": "127", "required";
101    Optional<i8> where "type": "number", "step": "1", "min": "-128", "max": "127";
102    i16 where "type": "number", "step": "1", "min": "-32768", "max": "32767", "required";
103    Optional<i16> where "type": "number", "step": "1", "min": "-32768", "max": "32767";
104    i32 where "type": "number", "step": "1", "min": "-2147483648", "max": "2147483647", "required";
105    Optional<i32> where "type": "number", "step": "1", "min": "-2147483648", "max": "2147483647";
106    i64 where "type": "number", "step": "1", "min": "-9223372036854775808", "max": "9223372036854775807", "required";
107    Optional<i64> where "type": "number", "step": "1", "min": "-9223372036854775808", "max": "9223372036854775807";
108    i128 where "type": "number", "step": "1", "min": "-170141183460469231731687303715884105728", "max": "170141183460469231731687303715884105727", "required";
109    Optional<i128> where "type": "number", "step": "1", "min": "-170141183460469231731687303715884105728", "max": "170141183460469231731687303715884105727";
110    String where "type": "text";
111    BigInt where "type": "number", "step": "1", "required";
112    Optional<BigInt> where "type": "number", "step": "1";
113    BigRational where "type": "number", "required";
114    Optional<BigRational> where "type": "number";
115    DateTime where "type": "datetime-local", "required";
116    Optional<DateTime> where "type": "datetime-local";
117    Date where "type": "date", "required";
118    Optional<Date> where "type": "date";
119    Time where "type": "time", "required";
120    Optional<Time> where "type": "time";
121    bool where "type": "checkbox";
122}
123
124/// Implements the required traits for creating a datatype.
125#[macro_export]
126macro_rules! impl_datatype {
127    ($this:ty) => {
128        impl std::str::FromStr for $this {
129            type Err = <$this as $crate::Datatype>::Error;
130        
131            fn from_str(s: &str) -> Result<Self, Self::Err> {
132                <$this as $crate::Datatype>::validate(<$this as $crate::Datatype>::Inner::from_str(s).map_err(<$this as $crate::Datatype>::Error::from)?)
133            }
134        }
135
136        impl<'de> serde::Deserialize<'de> for $this {
137            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138            where
139                D: serde::Deserializer<'de>,
140            {
141                let value = <$this as $crate::Datatype>::Inner::deserialize(deserializer)?;
142                <$this as $crate::Datatype>::validate(value).map_err(serde::de::Error::custom)
143            }
144        }
145    };
146
147    ($this:ty where $generic:ident : $sat:ident) => {
148        impl<$generic>  std::str::FromStr for $this
149        where
150            $generic: $sat
151        {
152            type Err = <$this as $crate::Datatype>::Error;
153        
154            fn from_str(s: &str) -> Result<Self, Self::Err> {
155                <$this as $crate::Datatype>::validate(<$this as $crate::Datatype>::Inner::from_str(s).map_err(<$this as $crate::Datatype>::Error::from)?)
156            }
157        }
158
159        impl<'de, $generic> serde::Deserialize<'de> for $this
160        where
161            $generic: $sat
162        {
163            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
164            where
165                D: serde::Deserializer<'de>,
166            {
167                let value = <$this as $crate::Datatype>::Inner::deserialize(deserializer)?;
168                <$this as $crate::Datatype>::validate(value).map_err(serde::de::Error::custom)
169            }
170        }
171    };
172}
173
174/// A trait for defining custom datatypes.
175/// Implemented on all types that can be used as a form input.
176pub trait Datatype: Clone + Display + Debug + Default + FromStr<Err = Self::Error> + Into<Self::Inner> + PartialEq + 'static {
177    type Inner: Datatype;
178    type Error: From<<Self::Inner as Datatype>::Error> + Error + Clone + PartialEq + 'static;
179
180    /// Validate the input and return the datatype.
181    fn validate(input: Self::Inner) -> Result<Self, Self::Error>
182    where
183        Self: Sized;
184
185    /// Return the HTML attributes for the datatype that should be added to an input field.
186    fn attributes() -> Vec<(&'static str, Attribute)>;
187}
188
189// Defines custom translations for a type `T`.
190// This is useful for adding custom error messages to error enums.
191#[derive(Clone)]
192pub(crate) struct TranslationProvider<T>(Rc<dyn Fn(T) -> TextProp>);
193
194
195/// Adds custom translations to a type `T`.
196/// This is useful for adding custom error messages to error enums or other elements.
197pub fn provide_translation<F, T>(f: F)
198where
199    T: Clone + 'static,
200    F: Fn(T) -> TextProp + 'static,
201{
202    provide_context(TranslationProvider(Rc::new(f)));
203}
204
205pub fn expect_translation<T, F>(value: F) -> TextProp
206where
207    F: Into<MaybeSignal<T>>,
208    T: Clone + 'static,
209{
210    let value = value.into();
211    let translation_provider = expect_context::<TranslationProvider<T>>();
212    (move || translation_provider.0(value.get()).get()).into()
213}
214
215pub fn use_translation<T, F>(value: F) -> TextProp
216where
217    F: Into<MaybeSignal<T>>,
218    T: Clone + Display + 'static,
219{
220    let value = value.into();
221    let translation_provider: Option<TranslationProvider<T>> = use_context::<TranslationProvider<T>>();
222    if let Some(translation_provider) = translation_provider {
223        (move || translation_provider.0(value.get()).get()).into()
224    } else {
225        (move || format!("{}", value.get())).into()
226    }
227}