1#![warn(missing_docs)]
2#![forbid(unsafe_code)]
3#![doc = include_str!("../README.md")]
4
5use facet_core::{Def, Facet, Type, UserType};
6use facet_reflect::Partial;
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<'facet>> {
74 let mut typed_partial = Partial::alloc::<T>()?;
75 {
76 let wip = typed_partial.inner_mut();
77 from_str_value(wip, urlencoded)?;
78 }
79 let boxed_value = typed_partial.build()?;
80 Ok(*boxed_value)
81}
82
83fn from_str_value<'mem, 'shape>(
87 wip: &mut Partial<'mem, 'shape>,
88 urlencoded: &str,
89) -> Result<(), UrlEncodedError<'shape>> {
90 trace!("Starting URL encoded form data deserialization");
91
92 let pairs = form_urlencoded::parse(urlencoded.as_bytes());
94
95 let mut nested_values = NestedValues::new();
97 for (key, value) in pairs {
98 nested_values.insert(&key, value.to_string());
99 }
100
101 initialize_nested_structures(&mut nested_values);
104
105 deserialize_value(wip, &nested_values)?;
107 Ok(())
108}
109
110fn initialize_nested_structures(nested: &mut NestedValues) {
113 for nested_value in nested.nested.values_mut() {
115 initialize_nested_structures(nested_value);
116 }
117}
118
119struct NestedValues {
121 flat: std::collections::HashMap<String, String>,
123 nested: std::collections::HashMap<String, NestedValues>,
125}
126
127impl NestedValues {
128 fn new() -> Self {
129 Self {
130 flat: std::collections::HashMap::new(),
131 nested: std::collections::HashMap::new(),
132 }
133 }
134
135 fn insert(&mut self, key: &str, value: String) {
136 if let Some(open_bracket) = key.find('[') {
138 if let Some(close_bracket) = key.find(']') {
139 if open_bracket < close_bracket {
140 let parent_key = &key[0..open_bracket];
141 let nested_key = &key[(open_bracket + 1)..close_bracket];
142 let remainder = &key[(close_bracket + 1)..];
143
144 let nested = self
145 .nested
146 .entry(parent_key.to_string())
147 .or_insert_with(NestedValues::new);
148
149 if remainder.is_empty() {
150 nested.flat.insert(nested_key.to_string(), value);
152 } else {
153 let new_key = format!("{}{}", nested_key, remainder);
155 nested.insert(&new_key, value);
156 }
157 return;
158 }
159 }
160 }
161
162 self.flat.insert(key.to_string(), value);
164 }
165
166 fn get(&self, key: &str) -> Option<&String> {
167 self.flat.get(key)
168 }
169
170 #[expect(dead_code)]
171 fn get_nested(&self, key: &str) -> Option<&NestedValues> {
172 self.nested.get(key)
173 }
174
175 fn keys(&self) -> impl Iterator<Item = &String> {
176 self.flat.keys()
177 }
178
179 #[expect(dead_code)]
180 fn nested_keys(&self) -> impl Iterator<Item = &String> {
181 self.nested.keys()
182 }
183}
184
185fn deserialize_value<'mem, 'shape>(
187 wip: &mut Partial<'mem, 'shape>,
188 values: &NestedValues,
189) -> Result<(), UrlEncodedError<'shape>> {
190 let shape = wip.shape();
191 match shape.ty {
192 Type::User(UserType::Struct(_)) => {
193 trace!("Deserializing struct");
194
195 for key in values.keys() {
197 if let Some(index) = wip.field_index(key) {
198 let value = values.get(key).unwrap(); wip.begin_nth_field(index)?;
200 deserialize_scalar_field(key, value, wip)?;
201 wip.end()?;
202 } else {
203 trace!("Unknown field: {}", key);
204 }
205 }
206
207 for key in values.nested.keys() {
209 if let Some(index) = wip.field_index(key) {
210 let nested_values = values.nested.get(key).unwrap(); wip.begin_nth_field(index)?;
212 deserialize_nested_field(key, nested_values, wip)?;
213 wip.end()?;
214 } else {
215 trace!("Unknown nested field: {}", key);
216 }
217 }
218
219 trace!("Finished deserializing struct");
220 Ok(())
221 }
222 _ => {
223 error!("Unsupported root type");
224 Err(UrlEncodedError::UnsupportedShape(
225 "Unsupported root type".to_string(),
226 ))
227 }
228 }
229}
230
231fn deserialize_scalar_field<'mem, 'shape>(
233 key: &str,
234 value: &str,
235 wip: &mut Partial<'mem, 'shape>,
236) -> Result<(), UrlEncodedError<'shape>> {
237 match wip.shape().def {
238 Def::Scalar(_sd) => {
239 if wip.shape().is_type::<String>() {
240 let s = value.to_string();
241 wip.set(s)?;
242 } else if wip.shape().is_type::<u64>() {
243 match value.parse::<u64>() {
244 Ok(num) => wip.set(num)?,
245 Err(_) => {
246 return Err(UrlEncodedError::InvalidNumber(
247 key.to_string(),
248 value.to_string(),
249 ));
250 }
251 };
252 } else {
253 warn!("facet-yaml: unsupported scalar type: {}", wip.shape());
254 return Err(UrlEncodedError::UnsupportedType(format!("{}", wip.shape())));
255 }
256 Ok(())
257 }
258 _ => {
259 error!("Expected scalar field");
260 Err(UrlEncodedError::UnsupportedShape(format!(
261 "Expected scalar for field '{}'",
262 key
263 )))
264 }
265 }
266}
267
268fn deserialize_nested_field<'mem, 'shape>(
270 key: &str,
271 nested_values: &NestedValues,
272 wip: &mut Partial<'mem, 'shape>,
273) -> Result<(), UrlEncodedError<'shape>> {
274 let shape = wip.shape();
275 match shape.ty {
276 Type::User(UserType::Struct(_)) => {
277 trace!("Deserializing nested struct field: {}", key);
278
279 for nested_key in nested_values.keys() {
281 if let Some(index) = wip.field_index(nested_key) {
282 let value = nested_values.get(nested_key).unwrap(); wip.begin_nth_field(index)?;
284 deserialize_scalar_field(nested_key, value, wip)?;
285 wip.end()?;
286 }
287 }
288
289 for nested_key in nested_values.nested.keys() {
291 if let Some(index) = wip.field_index(nested_key) {
292 let deeper_nested = nested_values.nested.get(nested_key).unwrap(); wip.begin_nth_field(index)?;
294 deserialize_nested_field(nested_key, deeper_nested, wip)?;
295 wip.end()?;
296 }
297 }
298
299 Ok(())
300 }
301 _ => {
302 error!("Expected struct field for nested value");
303 Err(UrlEncodedError::UnsupportedShape(format!(
304 "Expected struct for nested field '{}'",
305 key
306 )))
307 }
308 }
309}
310
311#[derive(Debug)]
313#[non_exhaustive]
314pub enum UrlEncodedError<'shape> {
315 InvalidNumber(String, String),
317 UnsupportedShape(String),
319 UnsupportedType(String),
321 ReflectError(facet_reflect::ReflectError<'shape>),
323}
324
325impl<'shape> From<facet_reflect::ReflectError<'shape>> for UrlEncodedError<'shape> {
326 fn from(err: facet_reflect::ReflectError<'shape>) -> Self {
327 UrlEncodedError::ReflectError(err)
328 }
329}
330
331impl<'shape> core::fmt::Display for UrlEncodedError<'shape> {
332 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
333 match self {
334 UrlEncodedError::InvalidNumber(field, value) => {
335 write!(f, "Invalid number for field '{}': '{}'", field, value)
336 }
337 UrlEncodedError::UnsupportedShape(shape) => {
338 write!(f, "Unsupported shape: {}", shape)
339 }
340 UrlEncodedError::UnsupportedType(ty) => {
341 write!(f, "Unsupported type: {}", ty)
342 }
343 UrlEncodedError::ReflectError(err) => {
344 write!(f, "Reflection error: {}", err)
345 }
346 }
347 }
348}
349
350impl<'shape> std::error::Error for UrlEncodedError<'shape> {}