nova_forms/
datatypes.rs

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