nova_forms/
form_data.rs

1use std::collections::{BTreeMap, HashMap};
2
3use leptos::*;
4use serde::{de::DeserializeOwned, Serialize};
5
6use crate::{QueryString, QueryStringPart};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub struct FormData(RwSignal<Data>);
10
11impl FormData {
12    pub fn new() -> Self {
13        Self(RwSignal::new(Data::new_group()))
14    }
15
16    pub fn from_data(data: Data) -> Self {
17        Self(RwSignal::new(data))
18    }
19
20    pub fn get(&self, qs: QueryString) -> Signal<Option<Data>> {
21        let self_singal = self.0;
22        Memo::new(move |_| {
23            self_singal.get().get(qs)
24        }).into()
25    }
26
27    pub fn set(&self, qs: QueryString, value: Data) {
28        self.0.update(|data| {
29            data.set(qs, value);
30        });
31    }
32
33    pub fn from_urlencoded(data: &str) -> Self {
34        Self::from_data(Data::from_urlencoded(data))
35    }
36
37    pub fn from<T: Serialize>(value: &T) -> Self {
38        Self::from_data(Data::from(value))
39    }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Hash)]
43pub enum Data {
44    Group(GroupData),
45    Input(InputData),
46}
47
48impl Data {
49    pub fn new_group() -> Self {
50        Data::Group(GroupData::new())
51    }
52
53    pub fn new_input(value: String) -> Self {
54        Data::Input(InputData::new(value))
55    }
56
57    fn from_vec(data: Vec<(QueryString, String)>) -> Self {
58        let mut parts = HashMap::new();
59
60        for (k, v) in data {
61            if let Some(head) = k.first() {
62                let vec: &mut Vec<(QueryString, String)> = parts
63                    .entry(head)
64                    .or_default();
65
66                vec.push((k.remove_first(), v));
67            } else {
68                return Data::Input(InputData(v));
69            }
70        }
71
72        let group = parts.into_iter()
73            .map(|(head, v)| {
74                (head, Data::from_vec(v))
75            })
76            .collect();
77
78            Data::Group(GroupData(group))
79    }
80
81    fn to_vec(&self) -> Vec<(QueryString, String)> {
82        fn visit(data: &Data, qs: QueryString, acc: &mut Vec<(QueryString, String)>) {
83            match data {
84                Data::Group(group) => {
85                    for (head, data) in &group.0 {
86                        visit(data, qs.add(*head), acc);
87                    }
88                }
89                Data::Input(value) => {
90                    acc.push((qs, value.0.clone()));
91                }
92            }
93        }
94
95        let mut acc = Vec::new();
96        visit(self, QueryString::default(), &mut acc);
97        acc
98    }
99
100    pub fn from_urlencoded(data: &str) -> Self {
101        let vec = data.split('&').into_iter()
102            .map(|part| part.split_once('=').map(|(k, v)| {
103                (QueryString::from(k), v.to_owned())
104            }).unwrap_or((QueryString::from(part), String::new())))
105            .collect::<Vec<_>>();
106
107        Self::from_vec(vec)
108    }
109
110    pub fn to_urlencoded(&self) -> String {
111        self.to_vec().into_iter()
112            .map(|(k, v)| {
113                format!("{}={}", k.to_string(), v)
114            })
115            .collect::<Vec<_>>()
116            .join("&")
117    }
118
119    pub fn to<T: DeserializeOwned>(&self) -> Result<T, serde_qs::Error> {
120        serde_qs::from_str(&self.to_urlencoded())
121    }
122
123    pub fn from<T: Serialize>(value: &T) -> Self {
124        let data = serde_qs::to_string(value).expect("failed to serialize form data");
125        Data::from_urlencoded(&data)
126    }
127
128    pub fn get(&self, qs: QueryString) -> Option<Data> {
129        if let Some(first) = qs.first() {
130            if let Data::Group(group) = self {
131                group.0.get(&first)?.get(qs.remove_first())
132            } else {
133                None
134            }
135        } else {
136            Some(self.clone())
137        }
138    }
139
140    pub fn set(&mut self, qs: QueryString, value: Data) {
141        if let Some(first) = qs.first() {
142            if let Data::Group(group) = self {
143                let data = group.0.entry(first).or_insert_with(|| Data::new_group());
144                data.set(qs.remove_first(), value);
145            }
146        } else {
147            *self = value;
148        }
149    }
150
151    pub fn len(&self) -> Option<usize> {
152        match self {
153            Data::Group(group) => Some(group.len()),
154            _ => None,
155        }
156    }
157
158    pub fn as_input(&self) -> Option<&InputData> {
159        match self {
160            Data::Input(value) => Some(value),
161            _ => None,
162        }
163    }
164
165    pub fn as_group(&self) -> Option<&GroupData> {
166        match self {
167            Data::Group(group) => Some(group),
168            _ => None,
169        }
170    }
171
172    pub fn into_input(self) -> Option<InputData> {
173        match self {
174            Data::Input(value) => Some(value),
175            _ => None,
176        }
177    }
178
179    pub fn into_group(self) -> Option<GroupData> {
180        match self {
181            Data::Group(group) => Some(group),
182            _ => None,
183        }
184    }
185
186}
187
188#[derive(Debug, Clone, PartialEq, Eq, Hash)]
189pub struct GroupData(BTreeMap<QueryStringPart, Data>);
190
191impl GroupData {
192    pub fn new() -> Self {
193        Self(BTreeMap::new())
194    }
195
196    pub fn len(&self) -> usize {
197        self.0.len()
198    }
199}
200
201#[derive(Debug, Clone, PartialEq, Eq, Hash)]
202pub struct InputData(String);
203
204impl InputData {
205    pub fn new(value: String) -> Self {
206        Self(value)
207    }
208
209    pub fn raw(&self) -> &str {
210        &self.0
211    }
212}
213
214
215#[cfg(test)]
216mod tests {
217    use serde::Deserialize;
218
219    use crate::qs;
220    use super::*;
221
222    #[test]
223    fn test_from_urlencoded() {
224        let _ = leptos::create_runtime();
225
226        let form_data = FormData::from_urlencoded("a=1&b=2&c=3");
227        assert_eq!(form_data.get(qs!(a)).get_untracked().unwrap().as_input().unwrap().raw(), "1");
228        assert_eq!(form_data.get(qs!(b)).get_untracked().unwrap().as_input().unwrap().raw(), "2");
229        assert_eq!(form_data.get(qs!(c)).get_untracked().unwrap().as_input().unwrap().raw(), "3");
230    }
231
232    #[test]
233    fn test_to_urlencoded() {
234        let _ = leptos::create_runtime();
235
236        let form_data = FormData::from_urlencoded("a=1&b=2&c=3");
237        assert_eq!(form_data.get(qs!(a)).get_untracked().unwrap().as_input().unwrap().raw(), "1");
238        assert_eq!(form_data.get(qs!(b)).get_untracked().unwrap().as_input().unwrap().raw(), "2");
239        assert_eq!(form_data.get(qs!(c)).get_untracked().unwrap().as_input().unwrap().raw(), "3");
240        assert_eq!(form_data.get(qs!()).get_untracked().unwrap().to_urlencoded(), "a=1&b=2&c=3");
241    }
242
243    
244    #[test]
245    fn test_set_value() {
246        let _ = leptos::create_runtime();
247
248        let form_data = FormData::from_urlencoded("a=1&b=2&c=3");
249        form_data.set(qs!(b), Data::new_input("7".to_owned()));
250        assert_eq!(form_data.get(qs!(a)).get_untracked().unwrap().as_input().unwrap().raw(), "1");
251        assert_eq!(form_data.get(qs!(b)).get_untracked().unwrap().as_input().unwrap().raw(), "7");
252        assert_eq!(form_data.get(qs!(c)).get_untracked().unwrap().as_input().unwrap().raw(), "3");
253
254    }
255
256    #[test]
257    fn test_values() {
258        let _ = leptos::create_runtime();
259
260        #[derive(Deserialize, PartialEq, Eq, Debug, Clone)]
261        struct Test {
262            a: i32,
263            b: i32,
264            c: i32,
265        }
266
267        let form_data = FormData::from_urlencoded("a=1&b=2&c=3");
268        assert_eq!(form_data.get(qs!()).get_untracked().unwrap().to::<Test>().unwrap(), Test { a: 1, b: 2, c: 3 });
269    }
270
271    #[test]
272    fn test_set_values() {
273        let _ = leptos::create_runtime();
274
275        #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
276        struct Test {
277            a: i32,
278            b: i32,
279            c: i32,
280        }
281
282        let form_data = FormData::from_urlencoded("a=1&b=2&c=3");
283        form_data.set(qs!(), Data::from(&Test { a: 4, b: 5, c: 6 }));
284        assert_eq!(form_data.get(qs!()).get_untracked().unwrap().to::<Test>().unwrap(), Test { a: 4, b: 5, c: 6 });
285    }
286
287    #[test]
288    fn test_len() {
289        let _ = leptos::create_runtime();
290
291        let form_data = FormData::from_urlencoded("a[0]=1&a[3]=21&a[2]=23&a[1]=3");
292        assert_eq!(form_data.get(qs!(a)).get_untracked().unwrap().as_group().unwrap().len(), 4);
293    }
294}