web_sys_query/query/
helpers.rs

1//! Helper Functions
2
3use crate::{
4    error::Error,
5    query::{Collection, Element},
6};
7use derive_more::{AsRef, Deref, DerefMut, From, Into};
8use std::iter::FromIterator;
9use web_sys::HtmlFormElement;
10
11impl Element {
12    /// Serialize a form into a data structure that holds a key-value
13    /// type.
14    ///
15    /// The generic return type can be anything that supports the
16    /// trait bounds, for example `Vec<(String, String)>` or
17    /// `HashMap<String, String>`.  To get a jQuery-compatible return
18    /// type, use the `FormData` type that is provided by this crate.
19    pub fn serialize_array<T, V>(&self) -> Result<T, Error>
20    where
21        T: FromIterator<(String, V)>,
22        V: From<String>,
23    {
24        let form = self.dyn_ref::<HtmlFormElement>()?;
25
26        Collection::from(form.elements())
27            .into_iter()
28            .filter(|elem| {
29                elem.attr("name").is_some()
30                    && !elem.is(":disabled").unwrap_or_default()
31                    && !((elem.is("option").unwrap_or_default()
32                        || elem.attr("type").as_deref() == Some("radio")
33                        || elem.attr("type").as_deref() == Some("checkbox"))
34                        && !elem.is(":checked").unwrap_or_default())
35            })
36            .map(|elem| {
37                let key = elem.attr("name").ok_or(Error::NoValue("name"))?;
38                let value = elem.val()?;
39                Ok((key, value.into()))
40            })
41            .collect::<Result<T, Error>>()
42    }
43}
44
45impl Collection {
46    /// Serialize a collection of forms into a data structure that
47    /// holds a key-value type.
48    ///
49    /// The generic return type can be anything that supports the
50    /// trait bounds, for example `Vec<(String, String)>` or
51    /// `HashMap<String, String>`.  To get a jQuery-compatible return
52    /// type, use the `FormData` type that is provided by this crate.
53    pub fn serialize_array<T, V>(&self) -> Result<T, Error>
54    where
55        T: FromIterator<(String, V)> + IntoIterator + FromIterator<<T as IntoIterator>::Item>,
56        V: From<String>,
57    {
58        Ok(self
59            .0
60            .iter()
61            .map(|elem| elem.serialize_array())
62            .collect::<Result<Vec<T>, Error>>()?
63            .into_iter()
64            .flatten()
65            .collect::<T>())
66    }
67}
68
69/// "name-value" representation of form data.
70///
71/// This struct can be used with the `.serialize_array()` function to
72/// get jquery-compatible name-value representation of the result.
73/// Some form elements, such as radio buttons or checkboxes, can
74/// contain multiple elements with the same name, so this array of
75/// name-value fields is returned instead of simple `HashMap`.
76#[derive(AsRef, Debug, Deref, DerefMut, Eq, From, PartialEq, Into)]
77#[cfg_attr(
78    feature = "serde-serialize",
79    derive(serde_derive::Serialize, serde_derive::Deserialize)
80)]
81pub struct FormData(Vec<FormValue>);
82
83impl FromIterator<(String, String)> for FormData {
84    fn from_iter<I: IntoIterator<Item = (String, String)>>(iter: I) -> Self {
85        Self(iter.into_iter().map(Into::into).collect())
86    }
87}
88
89impl FromIterator<FormValue> for FormData {
90    fn from_iter<I: IntoIterator<Item = FormValue>>(iter: I) -> Self {
91        Self(Vec::from_iter(iter))
92    }
93}
94
95impl IntoIterator for FormData {
96    type Item = FormValue;
97    type IntoIter = std::vec::IntoIter<Self::Item>;
98
99    fn into_iter(self) -> Self::IntoIter {
100        self.0.into_iter()
101    }
102}
103
104/// "name-value" representation of a single `FormData` field.
105#[derive(Debug, Eq, From, Into, PartialEq)]
106#[cfg_attr(
107    feature = "serde-serialize",
108    derive(serde_derive::Serialize, serde_derive::Deserialize)
109)]
110pub struct FormValue {
111    pub name: String,
112    pub value: String,
113}