Skip to main content

dioxus_html/events/
form.rs

1use crate::FileData;
2use crate::file_data::HasFileData;
3use std::fmt::Debug;
4
5use dioxus_core::Event;
6
7pub type FormEvent = Event<FormData>;
8
9/* DOMEvent:  Send + SyncTarget relatedTarget */
10pub struct FormData {
11    inner: Box<dyn HasFormData>,
12}
13
14impl FormData {
15    /// Create a new form event
16    pub fn new(event: impl HasFormData + 'static) -> Self {
17        Self {
18            inner: Box::new(event),
19        }
20    }
21
22    /// Get the value of the form event
23    pub fn value(&self) -> String {
24        self.inner.value()
25    }
26
27    /// Get the value of the form event as a parsed type
28    pub fn parsed<T>(&self) -> Result<T, T::Err>
29    where
30        T: std::str::FromStr,
31    {
32        self.value().parse()
33    }
34
35    /// Try to parse the value as a boolean
36    ///
37    /// Returns false if the value is not a boolean, or if it is false!
38    /// Does not verify anything about the event itself, use with caution
39    pub fn checked(&self) -> bool {
40        self.value().parse().unwrap_or(false)
41    }
42
43    /// Collect all the named form values from the containing form.
44    ///
45    /// Every input must be named!
46    pub fn values(&self) -> Vec<(String, FormValue)> {
47        self.inner.values()
48    }
49
50    /// Get the first value with the given name
51    pub fn get_first(&self, name: &str) -> Option<FormValue> {
52        self.values()
53            .into_iter()
54            .find_map(|(k, v)| if k == name { Some(v) } else { None })
55    }
56
57    /// Get all values with the given name
58    pub fn get(&self, name: &str) -> Vec<FormValue> {
59        self.values()
60            .into_iter()
61            .filter_map(|(k, v)| if k == name { Some(v) } else { None })
62            .collect()
63    }
64
65    /// Get the files of the form event
66    pub fn files(&self) -> Vec<FileData> {
67        self.inner.files()
68    }
69
70    /// Downcast this event to a concrete event type
71    #[inline(always)]
72    pub fn downcast<T: 'static>(&self) -> Option<&T> {
73        self.inner.as_any().downcast_ref::<T>()
74    }
75
76    /// Did this form pass its own validation?
77    pub fn valid(&self) -> bool {
78        !self.inner.value().is_empty()
79    }
80}
81
82impl FormData {
83    /// Parse the values into a struct with one field per value
84    #[cfg(feature = "serialize")]
85    pub fn parsed_values<T>(&self) -> Result<T, serde_json::Error>
86    where
87        T: serde::de::DeserializeOwned,
88    {
89        use crate::SerializedFileData;
90
91        let values = &self.values();
92
93        let mut map = serde_json::Map::new();
94        for (key, value) in values {
95            let entry = map
96                .entry(key.clone())
97                .or_insert_with(|| serde_json::Value::Array(Vec::new()));
98
99            match value {
100                FormValue::Text(text) => {
101                    entry
102                        .as_array_mut()
103                        .expect("entry should be an array")
104                        .push(serde_json::Value::String(text.clone()));
105                }
106                // we create the serialized variant with no bytes
107                // SerializedFileData, if given a real path, will read the bytes from disk (synchronously)
108                FormValue::File(Some(file_data)) => {
109                    let serialized = SerializedFileData::from_file_data(file_data);
110                    entry
111                        .as_array_mut()
112                        .expect("entry should be an array")
113                        .push(serde_json::to_value(&serialized).unwrap_or(serde_json::Value::Null));
114                }
115                FormValue::File(None) => {
116                    entry
117                        .as_array_mut()
118                        .expect("entry should be an array")
119                        .push(
120                            serde_json::to_value(SerializedFileData::empty())
121                                .unwrap_or(serde_json::Value::Null),
122                        );
123                }
124            }
125        }
126
127        // Go through the map and convert single-element arrays to just the element
128        let map = map
129            .into_iter()
130            .map(|(k, v)| match v {
131                serde_json::Value::Array(arr) if arr.len() == 1 => {
132                    (k, arr.into_iter().next().unwrap())
133                }
134                _ => (k, v),
135            })
136            .collect::<serde_json::Map<String, serde_json::Value>>();
137
138        serde_json::from_value(serde_json::Value::Object(map))
139    }
140}
141
142impl HasFileData for FormData {
143    fn files(&self) -> Vec<FileData> {
144        self.inner.files()
145    }
146}
147
148impl<E: HasFormData> From<E> for FormData {
149    fn from(e: E) -> Self {
150        Self { inner: Box::new(e) }
151    }
152}
153
154impl PartialEq for FormData {
155    fn eq(&self, other: &Self) -> bool {
156        self.value() == other.value() && self.values() == other.values()
157    }
158}
159
160impl Debug for FormData {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        f.debug_struct("FormEvent")
163            .field("value", &self.value())
164            .field("values", &self.values())
165            .field("valid", &self.valid())
166            .finish()
167    }
168}
169
170/// A value in a form, either text or a file
171#[derive(Debug, Clone, PartialEq)]
172pub enum FormValue {
173    Text(String),
174    File(Option<FileData>),
175}
176
177impl PartialEq<str> for FormValue {
178    fn eq(&self, other: &str) -> bool {
179        match self {
180            FormValue::Text(s) => s == other,
181            FormValue::File(_f) => false,
182        }
183    }
184}
185
186impl PartialEq<&str> for FormValue {
187    fn eq(&self, other: &&str) -> bool {
188        match self {
189            FormValue::Text(s) => s == other,
190            FormValue::File(_f) => false,
191        }
192    }
193}
194
195/// An object that has all the data for a form event
196pub trait HasFormData: HasFileData + std::any::Any {
197    fn value(&self) -> String;
198
199    fn valid(&self) -> bool;
200
201    fn values(&self) -> Vec<(String, FormValue)>;
202
203    /// return self as Any
204    fn as_any(&self) -> &dyn std::any::Any;
205}
206
207#[cfg(feature = "serialize")]
208pub use serialize::*;
209
210#[cfg(feature = "serialize")]
211mod serialize {
212    use crate::SerializedFileData;
213
214    use super::*;
215
216    /// A serialized form data object
217    #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
218    pub struct SerializedFormData {
219        #[serde(default)]
220        pub value: String,
221
222        #[serde(default)]
223        pub values: Vec<SerializedFormObject>,
224
225        #[serde(default)]
226        pub valid: bool,
227    }
228
229    #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
230    pub struct SerializedFormObject {
231        pub key: String,
232        pub text: Option<String>,
233        pub file: Option<SerializedFileData>,
234    }
235
236    #[cfg(feature = "serialize")]
237    impl SerializedFormData {
238        /// Create a new serialized form data object
239        pub fn new(value: String, values: Vec<SerializedFormObject>) -> Self {
240            Self {
241                value,
242                values,
243                valid: true,
244            }
245        }
246
247        /// Create a serialized form data object from a form data object
248        fn from_form_lossy(data: &FormData) -> Self {
249            if let Some(data) = data.downcast::<SerializedFormData>() {
250                return data.clone();
251            }
252
253            let values = data
254                .values()
255                .iter()
256                .map(|(key, value)| match value {
257                    FormValue::Text(s) => SerializedFormObject {
258                        key: key.clone(),
259                        text: Some(s.to_string()),
260                        file: None,
261                    },
262                    FormValue::File(f) => SerializedFormObject {
263                        key: key.clone(),
264                        text: None,
265                        file: Some(f.as_ref().map_or_else(
266                            SerializedFileData::empty,
267                            SerializedFileData::from_file_data,
268                        )),
269                    },
270                })
271                .collect();
272
273            Self {
274                values,
275                value: data.value(),
276                valid: data.valid(),
277            }
278        }
279    }
280
281    impl HasFormData for SerializedFormData {
282        fn value(&self) -> String {
283            self.value.clone()
284        }
285
286        fn values(&self) -> Vec<(String, FormValue)> {
287            self.values
288                .iter()
289                .map(|v| {
290                    let value = if let Some(text) = &v.text {
291                        FormValue::Text(text.clone())
292                    } else if let Some(_file) = &v.file {
293                        // todo: we lose the file contents here
294                        FormValue::File(None)
295                    } else {
296                        FormValue::File(None)
297                    };
298                    (v.key.clone(), value)
299                })
300                .collect()
301        }
302
303        fn valid(&self) -> bool {
304            self.valid
305        }
306
307        fn as_any(&self) -> &dyn std::any::Any {
308            self
309        }
310    }
311
312    impl HasFileData for SerializedFormData {
313        fn files(&self) -> Vec<FileData> {
314            self.values
315                .iter()
316                .filter_map(|v| v.file.as_ref().map(|f| FileData::new(f.clone())))
317                .collect()
318        }
319    }
320
321    impl serde::Serialize for FormData {
322        fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
323            SerializedFormData::from_form_lossy(self).serialize(serializer)
324        }
325    }
326
327    impl<'de> serde::Deserialize<'de> for FormData {
328        fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
329            let data = SerializedFormData::deserialize(deserializer)?;
330            Ok(Self {
331                inner: Box::new(data),
332            })
333        }
334    }
335}