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