1use std::{collections::HashMap, str::FromStr};
2
3use leptos::*;
4use percent_encoding::{percent_decode, percent_encode, NON_ALPHANUMERIC};
5use serde::{de::DeserializeOwned, Deserialize, Serialize};
6
7use crate::{QueryString, QueryStringPart};
8
9#[derive(Clone, Copy, Debug, Default, PartialEq)]
11pub struct FormData {
12 data: RwSignal<HashMap<QueryString, String>>
13}
14
15impl FormData {
16 pub fn serialize<F>(form_data: &F) -> Self
17 where
18 F: Serialize,
19 {
20 let serialized = serde_qs::to_string(form_data).expect("must be serializable");
21 FormData::from_str(&serialized).unwrap()
22 }
23
24 pub fn deserialize<F>(&self) -> F
25 where
26 F: DeserializeOwned,
27 {
28 serde_qs::from_str(&self.to_string()).expect("must be deserializable")
29 }
30}
31
32impl FromStr for FormData {
33 type Err = ();
34
35 fn from_str(s: &str) -> Result<Self, ()> {
36 let map = s
37 .split("&")
38 .into_iter()
39 .map(|pair| {
40 pair.split_once("=")
41 .map(|(k, v)| {
42 (
43 QueryString::from(k),
44 percent_decode(v.as_bytes()).decode_utf8_lossy().to_string(),
45 )
46 })
47 .unwrap_or_else(|| (QueryString::from(pair), String::new()))
48 })
49 .collect();
50
51 Ok(FormData {
52 data: RwSignal::new(map)
53 })
54 }
55}
56
57impl ToString for FormData {
58 fn to_string(&self) -> String {
59 self.data
60 .get()
61 .iter()
62 .map(|(k, v)| {
63 format!("{}={}",
64 k,
65 percent_encode(v.as_bytes(), NON_ALPHANUMERIC)
66 )
67 })
68 .collect::<Vec<_>>()
69 .join("&")
70 }
71}
72
73impl FormData {
74 pub fn values<T: DeserializeOwned + PartialEq>(&self, qs: QueryString) -> Signal<Option<T>> {
75 #[derive(Deserialize)]
76 struct Value<T> {
77 value: T,
78 }
79
80 let signal = self.data;
81
82 Memo::new(move |_| {
83 let data = signal.get();
84
85 let form_data = data
86 .iter()
87 .filter_map(|(k, v)| {
88 Some(format!("{}={}",
89 QueryString::default().add_key("value").join(k.extends(&qs)?),
90 percent_encode(v.as_bytes(), NON_ALPHANUMERIC)
91 ))
92 })
93 .collect::<Vec<_>>()
94 .join("&");
95
96 Some(serde_qs::from_str::<Value<T>>(&form_data.to_string()).ok()?.value)
97 }).into()
98 }
99
100 pub fn set_values<T: Serialize>(&self, qs: QueryString, values: T) {
101 self.data.update(|data| {
102 let form_data = FormData::serialize(&values);
103 for (k, v) in form_data.data.get_untracked() {
104 data.insert(qs.join(k), v);
105 }
106 });
107 }
108
109 pub fn raw_value(&self, qs: QueryString) -> Signal<String> {
110 let signal = self.data;
111
112 Memo::new(move |_| {
113 let data = signal.get();
114
115 let value = data
116 .iter()
117 .filter_map(|(k, v)| {
118 k.extends(&qs)?;
119 Some(v)
120 })
121 .next()
122 .cloned()
123 .unwrap_or_default();
124
125 value
126 }).into()
127 }
128
129 pub fn set_raw_value<S: Into<String>>(&self, qs: QueryString, value: S) {
130 self.data.update(|data| {
131 data.insert(qs.clone(), value.into());
132 });
133 }
134
135 pub fn value<T: FromStr>(&self, qs: QueryString) -> Signal<Result<T, T::Err>> {
136 let signal = self.raw_value(qs);
137 Signal::derive(move || T::from_str(&signal.get()))
138 }
139
140 pub fn set_value<T: ToString>(&self, qs: QueryString, value: T) {
141 self.set_raw_value(qs, value.to_string());
142 }
143
144 pub fn len(&self, qs: QueryString) -> Option<usize> {
145 self.data
146 .get_untracked()
147 .keys()
148 .map(|k| {
149 if let Some(k) = k.extends(&qs) {
150 k.iter().next().and_then(|&p| {
151 if let QueryStringPart::Index(i) = p {
152 Some(i)
153 } else {
154 None
155 }
156 })
157 } else {
158 None
159 }
160 })
161 .reduce(|l1, l2| {
162 if let (Some(l1), Some(l2)) = (l1, l2) {
163 Some(l1.max(l2))
164 } else {
165 None
166 }
167 })
168 .flatten()
169 .map(|l| l + 1)
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use crate::qs;
176 use super::*;
177
178 #[test]
179 fn test_value() {
180 let _ = leptos::create_runtime();
181
182 let form_data = FormData::from_str("a=1&b=2&c=3").unwrap();
183 assert_eq!(form_data.value::<i32>(qs!(a)).get_untracked().unwrap(), 1);
184 assert_eq!(form_data.value::<i32>(qs!(b)).get_untracked().unwrap(), 2);
185 assert_eq!(form_data.value::<i32>(qs!(c)).get_untracked().unwrap(), 3);
186 }
187
188 #[test]
189 fn test_set_value() {
190 let _ = leptos::create_runtime();
191
192 let form_data = FormData::from_str("a=1&b=2&c=3").unwrap();
193 form_data.set_value(QueryString::from("a"), 4);
194 assert_eq!(form_data.value::<i32>(qs!(a)).get_untracked().unwrap(), 4);
195 assert_eq!(form_data.value::<i32>(qs!(b)).get_untracked().unwrap(), 2);
196 assert_eq!(form_data.value::<i32>(qs!(c)).get_untracked().unwrap(), 3);
197 }
198
199 #[test]
200 fn test_values() {
201 let _ = leptos::create_runtime();
202
203 #[derive(Deserialize, PartialEq, Eq, Debug, Clone)]
204 struct Test {
205 a: i32,
206 b: i32,
207 c: i32,
208 }
209
210 let form_data = FormData::from_str("a=1&b=2&c=3").unwrap();
211 assert_eq!(form_data.values::<Test>(qs!()).get_untracked().unwrap(), Test { a: 1, b: 2, c: 3 });
212 }
213
214 #[test]
215 fn test_set_values() {
216 let _ = leptos::create_runtime();
217
218 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
219 struct Test {
220 a: i32,
221 b: i32,
222 c: i32,
223 }
224
225 let form_data = FormData::from_str("a=1&b=2&c=3").unwrap();
226 form_data.set_values(qs!(), Test { a: 4, b: 5, c: 6 });
227 assert_eq!(form_data.values::<Test>(qs!()).get_untracked().unwrap(), Test { a: 4, b: 5, c: 6 });
228 }
229
230 #[test]
231 fn test_len() {
232 let _ = leptos::create_runtime();
233
234 let form_data = FormData::from_str("a[0]=1&a[3]=2&a[1]=3").unwrap();
235 assert_eq!(form_data.len(qs!(a)).unwrap(), 4);
236 }
237}