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}