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