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::{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<'facet>> {
74 let val = from_str_value(Wip::alloc::<T>()?, urlencoded)?;
75 Ok(val.materialize::<T>()?)
76}
77
78fn from_str_value<'mem, 'shape>(
82 wip: Wip<'mem, 'shape>,
83 urlencoded: &str,
84) -> Result<HeapValue<'mem, 'shape>, UrlEncodedError<'shape>> {
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, 'shape>(
181 wip: Wip<'mem, 'shape>,
182 values: &NestedValues,
183) -> Result<HeapValue<'mem, 'shape>, UrlEncodedError<'shape>> {
184 let shape = wip.shape();
185 match shape.ty {
186 Type::User(UserType::Struct(_)) => {
187 trace!("Deserializing struct");
188
189 let mut wip = wip;
190
191 for key in values.keys() {
193 if let Some(index) = wip.field_index(key) {
194 let value = values.get(key).unwrap(); let field = wip.field(index)?;
196 wip = deserialize_scalar_field(key, value, field)?;
197 } else {
198 trace!("Unknown field: {}", key);
199 }
200 }
201
202 for key in values.nested.keys() {
204 if let Some(index) = wip.field_index(key) {
205 let nested_values = values.nested.get(key).unwrap(); let field = wip.field(index)?;
207 wip = deserialize_nested_field(key, nested_values, field)?;
208 } else {
209 trace!("Unknown nested field: {}", key);
210 }
211 }
212
213 trace!("Finished deserializing struct");
214 Ok(wip.build()?)
215 }
216 _ => {
217 error!("Unsupported root type");
218 Err(UrlEncodedError::UnsupportedShape(
219 "Unsupported root type".to_string(),
220 ))
221 }
222 }
223}
224
225fn deserialize_scalar_field<'mem, 'shape>(
227 key: &str,
228 value: &str,
229 wip: Wip<'mem, 'shape>,
230) -> Result<Wip<'mem, 'shape>, UrlEncodedError<'shape>> {
231 match wip.shape().def {
232 Def::Scalar(_sd) => {
233 let wip = if wip.shape().is_type::<String>() {
234 let s = value.to_string();
235 wip.put(s)?
236 } else if wip.shape().is_type::<u64>() {
237 match value.parse::<u64>() {
238 Ok(num) => wip.put(num)?,
239 Err(_) => {
240 return Err(UrlEncodedError::InvalidNumber(
241 key.to_string(),
242 value.to_string(),
243 ));
244 }
245 }
246 } else {
247 warn!("facet-yaml: unsupported scalar type: {}", wip.shape());
248 return Err(UrlEncodedError::UnsupportedType(format!("{}", wip.shape())));
249 };
250 Ok(wip.pop()?)
251 }
252 _ => {
253 error!("Expected scalar field");
254 Err(UrlEncodedError::UnsupportedShape(format!(
255 "Expected scalar for field '{}'",
256 key
257 )))
258 }
259 }
260}
261
262fn deserialize_nested_field<'mem, 'shape>(
264 key: &str,
265 nested_values: &NestedValues,
266 wip: Wip<'mem, 'shape>,
267) -> Result<Wip<'mem, 'shape>, UrlEncodedError<'shape>> {
268 let shape = wip.shape();
269 match shape.ty {
270 Type::User(UserType::Struct(_)) => {
271 trace!("Deserializing nested struct field: {}", key);
272
273 let mut current_wip = wip;
274
275 for nested_key in nested_values.keys() {
277 if let Some(index) = current_wip.field_index(nested_key) {
278 let value = nested_values.get(nested_key).unwrap(); let field_wip = current_wip.field(index)?;
280 current_wip = deserialize_scalar_field(nested_key, value, field_wip)?
281 }
282 }
283
284 for nested_key in nested_values.nested.keys() {
286 if let Some(index) = current_wip.field_index(nested_key) {
287 let deeper_nested = nested_values.nested.get(nested_key).unwrap(); let field_wip = current_wip.field(index)?;
289 current_wip = deserialize_nested_field(nested_key, deeper_nested, field_wip)?;
290 }
291 }
292
293 Ok(current_wip.pop()?)
295 }
296 _ => {
297 error!("Expected struct field for nested value");
298 Err(UrlEncodedError::UnsupportedShape(format!(
299 "Expected struct for nested field '{}'",
300 key
301 )))
302 }
303 }
304}
305
306#[derive(Debug)]
308#[non_exhaustive]
309pub enum UrlEncodedError<'shape> {
310 InvalidNumber(String, String),
312 UnsupportedShape(String),
314 UnsupportedType(String),
316 ReflectError(facet_reflect::ReflectError<'shape>),
318}
319
320impl<'shape> From<facet_reflect::ReflectError<'shape>> for UrlEncodedError<'shape> {
321 fn from(err: facet_reflect::ReflectError<'shape>) -> Self {
322 UrlEncodedError::ReflectError(err)
323 }
324}
325
326impl<'shape> core::fmt::Display for UrlEncodedError<'shape> {
327 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
328 match self {
329 UrlEncodedError::InvalidNumber(field, value) => {
330 write!(f, "Invalid number for field '{}': '{}'", field, value)
331 }
332 UrlEncodedError::UnsupportedShape(shape) => {
333 write!(f, "Unsupported shape: {}", shape)
334 }
335 UrlEncodedError::UnsupportedType(ty) => {
336 write!(f, "Unsupported type: {}", ty)
337 }
338 UrlEncodedError::ReflectError(err) => {
339 write!(f, "Reflection error: {}", err)
340 }
341 }
342 }
343}
344
345impl<'shape> std::error::Error for UrlEncodedError<'shape> {}