dioxus_html/events/
form.rs1use crate::FileData;
2use crate::file_data::HasFileData;
3use std::fmt::Debug;
4
5use dioxus_core::Event;
6
7pub type FormEvent = Event<FormData>;
8
9pub struct FormData {
11 inner: Box<dyn HasFormData>,
12}
13
14impl FormData {
15 pub fn new(event: impl HasFormData + 'static) -> Self {
17 Self {
18 inner: Box::new(event),
19 }
20 }
21
22 pub fn value(&self) -> String {
24 self.inner.value()
25 }
26
27 pub fn parsed<T>(&self) -> Result<T, T::Err>
29 where
30 T: std::str::FromStr,
31 {
32 self.value().parse()
33 }
34
35 pub fn checked(&self) -> bool {
40 self.value().parse().unwrap_or(false)
41 }
42
43 pub fn values(&self) -> Vec<(String, FormValue)> {
47 self.inner.values()
48 }
49
50 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 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 pub fn files(&self) -> Vec<FileData> {
67 self.inner.files()
68 }
69
70 #[inline(always)]
72 pub fn downcast<T: 'static>(&self) -> Option<&T> {
73 self.inner.as_any().downcast_ref::<T>()
74 }
75
76 pub fn valid(&self) -> bool {
78 !self.inner.value().is_empty()
79 }
80}
81
82impl FormData {
83 #[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 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 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#[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
195pub 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 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 #[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 pub fn new(value: String, values: Vec<SerializedFormObject>) -> Self {
240 Self {
241 value,
242 values,
243 valid: true,
244 }
245 }
246
247 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 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}