1#![warn(missing_docs)]
2#![forbid(unsafe_code)]
3#![doc = include_str!("../README.md")]
4
5use facet_core::{Def, Facet};
6use facet_reflect::{HeapValue, Wip};
7use log::*;
8
9#[cfg(test)]
10mod tests;
11
12pub fn from_str<'input: 'facet, 'facet, T: Facet<'facet>>(
72 urlencoded: &'input str,
73) -> Result<T, UrlEncodedError> {
74 let val = from_str_value(Wip::alloc::<T>()?, urlencoded)?;
75 Ok(val.materialize::<T>()?)
76}
77
78fn from_str_value<'mem>(
82 wip: Wip<'mem>,
83 urlencoded: &str,
84) -> Result<HeapValue<'mem>, UrlEncodedError> {
85 trace!("Starting URL encoded form data deserialization");
86
87 let pairs = form_urlencoded::parse(urlencoded.as_bytes());
89
90 let mut nested_values = NestedValues::new();
92 for (key, value) in pairs {
93 nested_values.insert(&key, value.to_string());
94 }
95
96 initialize_nested_structures(&mut nested_values);
99
100 deserialize_value(wip, &nested_values)
102}
103
104fn initialize_nested_structures(nested: &mut NestedValues) {
107 for nested_value in nested.nested.values_mut() {
109 initialize_nested_structures(nested_value);
110 }
111}
112
113struct NestedValues {
115 flat: std::collections::HashMap<String, String>,
117 nested: std::collections::HashMap<String, NestedValues>,
119}
120
121impl NestedValues {
122 fn new() -> Self {
123 Self {
124 flat: std::collections::HashMap::new(),
125 nested: std::collections::HashMap::new(),
126 }
127 }
128
129 fn insert(&mut self, key: &str, value: String) {
130 if let Some(open_bracket) = key.find('[') {
132 if let Some(close_bracket) = key.find(']') {
133 if open_bracket < close_bracket {
134 let parent_key = &key[0..open_bracket];
135 let nested_key = &key[(open_bracket + 1)..close_bracket];
136 let remainder = &key[(close_bracket + 1)..];
137
138 let nested = self
139 .nested
140 .entry(parent_key.to_string())
141 .or_insert_with(NestedValues::new);
142
143 if remainder.is_empty() {
144 nested.flat.insert(nested_key.to_string(), value);
146 } else {
147 let new_key = format!("{}{}", nested_key, remainder);
149 nested.insert(&new_key, value);
150 }
151 return;
152 }
153 }
154 }
155
156 self.flat.insert(key.to_string(), value);
158 }
159
160 fn get(&self, key: &str) -> Option<&String> {
161 self.flat.get(key)
162 }
163
164 #[expect(dead_code)]
165 fn get_nested(&self, key: &str) -> Option<&NestedValues> {
166 self.nested.get(key)
167 }
168
169 fn keys(&self) -> impl Iterator<Item = &String> {
170 self.flat.keys()
171 }
172
173 #[expect(dead_code)]
174 fn nested_keys(&self) -> impl Iterator<Item = &String> {
175 self.nested.keys()
176 }
177}
178
179fn deserialize_value<'mem>(
181 wip: Wip<'mem>,
182 values: &NestedValues,
183) -> Result<HeapValue<'mem>, UrlEncodedError> {
184 match wip.shape().def {
185 Def::Struct(_sd) => {
186 trace!("Deserializing struct");
187
188 let mut wip = wip;
189
190 for key in values.keys() {
192 if let Some(index) = wip.field_index(key) {
193 let value = values.get(key).unwrap(); let field = wip.field(index)?;
195 wip = deserialize_scalar_field(key, value, field)?;
196 } else {
197 trace!("Unknown field: {}", key);
198 }
199 }
200
201 for key in values.nested.keys() {
203 if let Some(index) = wip.field_index(key) {
204 let nested_values = values.nested.get(key).unwrap(); let field = wip.field(index)?;
206 wip = deserialize_nested_field(key, nested_values, field)?;
207 } else {
208 trace!("Unknown nested field: {}", key);
209 }
210 }
211
212 trace!("Finished deserializing struct");
213 Ok(wip.build()?)
214 }
215 _ => {
216 error!("Unsupported root type");
217 Err(UrlEncodedError::UnsupportedShape(
218 "Unsupported root type".to_string(),
219 ))
220 }
221 }
222}
223
224fn deserialize_scalar_field<'mem>(
226 key: &str,
227 value: &str,
228 wip: Wip<'mem>,
229) -> Result<Wip<'mem>, UrlEncodedError> {
230 match wip.shape().def {
231 Def::Scalar(_sd) => {
232 let wip = if wip.shape().is_type::<String>() {
233 let s = value.to_string();
234 wip.put(s)?
235 } else if wip.shape().is_type::<u64>() {
236 match value.parse::<u64>() {
237 Ok(num) => wip.put(num)?,
238 Err(_) => {
239 return Err(UrlEncodedError::InvalidNumber(
240 key.to_string(),
241 value.to_string(),
242 ));
243 }
244 }
245 } else {
246 warn!("Unsupported scalar type: {}", wip.shape());
247 return Err(UrlEncodedError::UnsupportedType(format!("{}", wip.shape())));
248 };
249 Ok(wip.pop()?)
250 }
251 _ => {
252 error!("Expected scalar field");
253 Err(UrlEncodedError::UnsupportedShape(format!(
254 "Expected scalar for field '{}'",
255 key
256 )))
257 }
258 }
259}
260
261fn deserialize_nested_field<'mem>(
263 key: &str,
264 nested_values: &NestedValues,
265 wip: Wip<'mem>,
266) -> Result<Wip<'mem>, UrlEncodedError> {
267 match wip.shape().def {
268 Def::Struct(_sd) => {
269 trace!("Deserializing nested struct field: {}", key);
270
271 let mut current_wip = wip;
272
273 for nested_key in nested_values.keys() {
275 if let Some(index) = current_wip.field_index(nested_key) {
276 let value = nested_values.get(nested_key).unwrap(); let field_wip = current_wip.field(index)?;
278 current_wip = deserialize_scalar_field(nested_key, value, field_wip)?
279 }
280 }
281
282 for nested_key in nested_values.nested.keys() {
284 if let Some(index) = current_wip.field_index(nested_key) {
285 let deeper_nested = nested_values.nested.get(nested_key).unwrap(); let field_wip = current_wip.field(index)?;
287 current_wip = deserialize_nested_field(nested_key, deeper_nested, field_wip)?;
288 }
289 }
290
291 Ok(current_wip.pop()?)
293 }
294 _ => {
295 error!("Expected struct field for nested value");
296 Err(UrlEncodedError::UnsupportedShape(format!(
297 "Expected struct for nested field '{}'",
298 key
299 )))
300 }
301 }
302}
303
304#[derive(Debug)]
306#[non_exhaustive]
307pub enum UrlEncodedError {
308 InvalidNumber(String, String),
310 UnsupportedShape(String),
312 UnsupportedType(String),
314 ReflectError(facet_reflect::ReflectError),
316}
317
318impl From<facet_reflect::ReflectError> for UrlEncodedError {
319 fn from(err: facet_reflect::ReflectError) -> Self {
320 UrlEncodedError::ReflectError(err)
321 }
322}
323
324impl core::fmt::Display for UrlEncodedError {
325 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
326 match self {
327 UrlEncodedError::InvalidNumber(field, value) => {
328 write!(f, "Invalid number for field '{}': '{}'", field, value)
329 }
330 UrlEncodedError::UnsupportedShape(shape) => {
331 write!(f, "Unsupported shape: {}", shape)
332 }
333 UrlEncodedError::UnsupportedType(ty) => {
334 write!(f, "Unsupported type: {}", ty)
335 }
336 UrlEncodedError::ReflectError(err) => {
337 write!(f, "Reflection error: {}", err)
338 }
339 }
340 }
341}
342
343impl std::error::Error for UrlEncodedError {}