1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4use facet_core::{Def, Facet, Type, UserType};
5use facet_reflect::{Partial, ReflectError};
6use log::*;
7
8#[cfg(test)]
9mod tests;
10
11mod form;
12pub use form::Form;
13
14mod query;
15pub use query::Query;
16
17#[cfg(feature = "axum")]
18mod axum;
19#[cfg(feature = "axum")]
20pub use self::axum::{FormRejection, QueryRejection};
21
22pub fn from_str<'input: 'facet, 'facet, T: Facet<'facet>>(
82 urlencoded: &'input str,
83) -> Result<T, UrlEncodedError> {
84 let partial = Partial::alloc::<T>()?;
85 let partial = from_str_value(partial, urlencoded)?;
86 let result: T = partial.build()?.materialize()?;
87 Ok(result)
88}
89
90pub fn from_str_owned<T: Facet<'static>>(urlencoded: &str) -> Result<T, UrlEncodedError> {
113 let partial = Partial::alloc::<T>()?;
114 let partial = from_str_value(partial, urlencoded)?;
115 let result: T = partial.build()?.materialize()?;
116 Ok(result)
117}
118
119fn from_str_value<'mem>(
123 mut wip: Partial<'mem>,
124 urlencoded: &str,
125) -> Result<Partial<'mem>, UrlEncodedError> {
126 trace!("Starting URL encoded form data deserialization");
127
128 let pairs = form_urlencoded::parse(urlencoded.as_bytes());
130
131 let mut nested_values = NestedValues::new();
133 for (key, value) in pairs {
134 nested_values.insert(&key, value.to_string());
135 }
136
137 initialize_nested_structures(&mut nested_values);
140
141 wip = deserialize_value(wip, &nested_values)?;
143 Ok(wip)
144}
145
146fn initialize_nested_structures(nested: &mut NestedValues) {
149 for nested_value in nested.nested.values_mut() {
151 initialize_nested_structures(nested_value);
152 }
153}
154
155struct NestedValues {
157 flat: std::collections::HashMap<String, String>,
159 nested: std::collections::HashMap<String, NestedValues>,
161}
162
163impl NestedValues {
164 fn new() -> Self {
165 Self {
166 flat: std::collections::HashMap::new(),
167 nested: std::collections::HashMap::new(),
168 }
169 }
170
171 fn insert(&mut self, key: &str, value: String) {
172 if let Some(open_bracket) = key.find('[')
174 && let Some(close_bracket) = key.find(']')
175 && open_bracket < close_bracket
176 {
177 let parent_key = &key[0..open_bracket];
178 let nested_key = &key[(open_bracket + 1)..close_bracket];
179 let remainder = &key[(close_bracket + 1)..];
180
181 let nested = self
182 .nested
183 .entry(parent_key.to_string())
184 .or_insert_with(NestedValues::new);
185
186 if remainder.is_empty() {
187 nested.flat.insert(nested_key.to_string(), value);
189 } else {
190 let new_key = format!("{nested_key}{remainder}");
192 nested.insert(&new_key, value);
193 }
194 return;
195 }
196
197 self.flat.insert(key.to_string(), value);
199 }
200
201 fn get(&self, key: &str) -> Option<&String> {
202 self.flat.get(key)
203 }
204
205 #[expect(dead_code)]
206 fn get_nested(&self, key: &str) -> Option<&NestedValues> {
207 self.nested.get(key)
208 }
209
210 fn keys(&self) -> impl Iterator<Item = &String> {
211 self.flat.keys()
212 }
213
214 #[expect(dead_code)]
215 fn nested_keys(&self) -> impl Iterator<Item = &String> {
216 self.nested.keys()
217 }
218}
219
220fn deserialize_value<'mem>(
222 mut wip: Partial<'mem>,
223 values: &NestedValues,
224) -> Result<Partial<'mem>, UrlEncodedError> {
225 let shape = wip.shape();
226 match shape.ty {
227 Type::User(UserType::Struct(_)) => {
228 trace!("Deserializing struct");
229
230 for key in values.keys() {
232 if let Some(index) = wip.field_index(key) {
233 let value = values.get(key).unwrap(); wip = wip.begin_nth_field(index)?;
235 wip = deserialize_scalar_field(key, value, wip)?;
236 wip = wip.end()?;
237 } else {
238 trace!("Unknown field: {key}");
239 }
240 }
241
242 for key in values.nested.keys() {
244 if let Some(index) = wip.field_index(key) {
245 let nested_values = values.nested.get(key).unwrap(); wip = wip.begin_nth_field(index)?;
247 wip = deserialize_nested_field(key, nested_values, wip)?;
248 wip = wip.end()?;
249 } else {
250 trace!("Unknown nested field: {key}");
251 }
252 }
253
254 trace!("Finished deserializing struct");
255 Ok(wip)
256 }
257 _ => {
258 error!("Unsupported root type");
259 Err(UrlEncodedError::UnsupportedShape(
260 "Unsupported root type".to_string(),
261 ))
262 }
263 }
264}
265
266fn deserialize_scalar_field<'mem>(
268 key: &str,
269 value: &str,
270 mut wip: Partial<'mem>,
271) -> Result<Partial<'mem>, UrlEncodedError> {
272 match wip.shape().def {
273 Def::Scalar => {
274 if wip.shape().is_type::<String>() {
275 let s = value.to_string();
276 wip = wip.set(s)?;
277 } else if wip.shape().is_type::<u64>() {
278 match value.parse::<u64>() {
279 Ok(num) => wip = wip.set(num)?,
280 Err(_) => {
281 return Err(UrlEncodedError::InvalidNumber(
282 key.to_string(),
283 value.to_string(),
284 ));
285 }
286 };
287 } else {
288 warn!("facet-yaml: unsupported scalar type: {}", wip.shape());
289 return Err(UrlEncodedError::UnsupportedType(format!("{}", wip.shape())));
290 }
291 Ok(wip)
292 }
293 _ => {
294 error!("Expected scalar field");
295 Err(UrlEncodedError::UnsupportedShape(format!(
296 "Expected scalar for field '{key}'"
297 )))
298 }
299 }
300}
301
302fn deserialize_nested_field<'mem>(
304 key: &str,
305 nested_values: &NestedValues,
306 mut wip: Partial<'mem>,
307) -> Result<Partial<'mem>, UrlEncodedError> {
308 let shape = wip.shape();
309 match shape.ty {
310 Type::User(UserType::Struct(_)) => {
311 trace!("Deserializing nested struct field: {key}");
312
313 for nested_key in nested_values.keys() {
315 if let Some(index) = wip.field_index(nested_key) {
316 let value = nested_values.get(nested_key).unwrap(); wip = wip.begin_nth_field(index)?;
318 wip = deserialize_scalar_field(nested_key, value, wip)?;
319 wip = wip.end()?;
320 }
321 }
322
323 for nested_key in nested_values.nested.keys() {
325 if let Some(index) = wip.field_index(nested_key) {
326 let deeper_nested = nested_values.nested.get(nested_key).unwrap(); wip = wip.begin_nth_field(index)?;
328 wip = deserialize_nested_field(nested_key, deeper_nested, wip)?;
329 wip = wip.end()?;
330 }
331 }
332
333 Ok(wip)
334 }
335 _ => {
336 error!("Expected struct field for nested value");
337 Err(UrlEncodedError::UnsupportedShape(format!(
338 "Expected struct for nested field '{key}'"
339 )))
340 }
341 }
342}
343
344#[derive(Debug)]
346pub enum UrlEncodedError {
347 InvalidNumber(String, String),
349 UnsupportedShape(String),
351 UnsupportedType(String),
353 ReflectError(ReflectError),
355}
356
357impl From<ReflectError> for UrlEncodedError {
358 fn from(err: ReflectError) -> Self {
359 UrlEncodedError::ReflectError(err)
360 }
361}
362
363impl core::fmt::Display for UrlEncodedError {
364 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
365 match self {
366 UrlEncodedError::InvalidNumber(field, value) => {
367 write!(f, "Invalid number for field '{field}': '{value}'")
368 }
369 UrlEncodedError::UnsupportedShape(shape) => {
370 write!(f, "Unsupported shape: {shape}")
371 }
372 UrlEncodedError::UnsupportedType(ty) => {
373 write!(f, "Unsupported type: {ty}")
374 }
375 UrlEncodedError::ReflectError(err) => {
376 write!(f, "Reflection error: {err}")
377 }
378 }
379 }
380}
381
382impl std::error::Error for UrlEncodedError {}