1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4use facet_core::{Facet, Opaque};
5use facet_reflect::{PokeStruct, PokeUninit};
6use log::*;
7
8#[cfg(test)]
9mod tests;
10
11pub fn from_str<T: Facet>(urlencoded: &str) -> Result<T, UrlEncodedError> {
71 let (poke, _guard) = PokeUninit::alloc::<T>();
72 let opaque = from_str_opaque(poke, urlencoded)?;
73 Ok(unsafe { opaque.read::<T>() })
74}
75
76fn from_str_opaque<'mem>(
80 poke: PokeUninit<'mem>,
81 urlencoded: &str,
82) -> Result<Opaque<'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 deserialize_value(poke, &nested_values)
96}
97
98struct NestedValues {
100 flat: std::collections::HashMap<String, String>,
102 nested: std::collections::HashMap<String, NestedValues>,
104}
105
106impl NestedValues {
107 fn new() -> Self {
108 Self {
109 flat: std::collections::HashMap::new(),
110 nested: std::collections::HashMap::new(),
111 }
112 }
113
114 fn insert(&mut self, key: &str, value: String) {
115 if let Some(open_bracket) = key.find('[') {
117 if let Some(close_bracket) = key.find(']') {
118 if open_bracket < close_bracket {
119 let parent_key = &key[0..open_bracket];
120 let nested_key = &key[(open_bracket + 1)..close_bracket];
121 let remainder = &key[(close_bracket + 1)..];
122
123 let nested = self
124 .nested
125 .entry(parent_key.to_string())
126 .or_insert_with(NestedValues::new);
127
128 if remainder.is_empty() {
129 nested.flat.insert(nested_key.to_string(), value);
131 } else {
132 let new_key = format!("{}{}", nested_key, remainder);
134 nested.insert(&new_key, value);
135 }
136 return;
137 }
138 }
139 }
140
141 self.flat.insert(key.to_string(), value);
143 }
144
145 fn get(&self, key: &str) -> Option<&String> {
146 self.flat.get(key)
147 }
148
149 fn get_nested(&self, key: &str) -> Option<&NestedValues> {
150 self.nested.get(key)
151 }
152
153 fn keys(&self) -> impl Iterator<Item = &String> {
154 self.flat.keys()
155 }
156
157 fn nested_keys(&self) -> impl Iterator<Item = &String> {
158 self.nested.keys()
159 }
160}
161
162fn deserialize_value<'mem>(
164 poke: PokeUninit<'mem>,
165 values: &NestedValues,
166) -> Result<Opaque<'mem>, UrlEncodedError> {
167 match poke {
168 PokeUninit::Struct(mut ps) => {
169 trace!("Deserializing struct");
170
171 for key in values.keys() {
173 if let Ok((index, field_poke)) = ps.field_by_name(key) {
174 let value = values.get(key).unwrap(); deserialize_scalar_field(key, value, field_poke, index, &mut ps)?;
176 } else {
177 warn!("Unknown field: {}", key);
178 }
180 }
181
182 for key in values.nested_keys() {
184 if let Ok((index, field_poke)) = ps.field_by_name(key) {
185 if let Some(nested_values) = values.get_nested(key) {
186 match field_poke {
187 PokeUninit::Struct(_) => {
188 let _nested_opaque = deserialize_value(field_poke, nested_values)?;
189 unsafe {
190 ps.mark_initialized(index);
191 }
192 }
193 _ => {
194 return Err(UrlEncodedError::UnsupportedShape(format!(
195 "Expected struct for nested field '{}'",
196 key
197 )));
198 }
199 }
200 }
201 } else {
202 warn!("Unknown nested field: {}", key);
203 }
205 }
206
207 trace!("Finished deserializing struct");
208 Ok(ps.build_in_place())
209 }
210 _ => {
211 error!("Unsupported root type");
212 Err(UrlEncodedError::UnsupportedShape(
213 "Unsupported root type".to_string(),
214 ))
215 }
216 }
217}
218
219fn deserialize_scalar_field<'mem>(
221 key: &str,
222 value: &str,
223 field_poke: PokeUninit<'mem>,
224 index: usize,
225 ps: &mut PokeStruct<'mem>,
226) -> Result<(), UrlEncodedError> {
227 match field_poke {
228 PokeUninit::Scalar(ps_scalar) => {
229 if ps_scalar.shape().is_type::<String>() {
230 let s = value.to_string();
231 ps_scalar.put(s);
232 } else if ps_scalar.shape().is_type::<u64>() {
233 match value.parse::<u64>() {
234 Ok(num) => {
235 ps_scalar.put(num);
236 }
237 Err(_) => {
238 return Err(UrlEncodedError::InvalidNumber(
239 key.to_string(),
240 value.to_string(),
241 ));
242 }
243 }
244 } else {
245 warn!("Unsupported scalar type: {}", ps_scalar.shape());
246 return Err(UrlEncodedError::UnsupportedType(format!(
247 "{}",
248 ps_scalar.shape()
249 )));
250 }
251 unsafe { ps.mark_initialized(index) };
252 Ok(())
253 }
254 _ => {
255 error!("Expected scalar field");
256 Err(UrlEncodedError::UnsupportedShape(format!(
257 "Expected scalar for field '{}'",
258 key
259 )))
260 }
261 }
262}
263
264#[derive(Debug)]
266#[non_exhaustive]
267pub enum UrlEncodedError {
268 InvalidNumber(String, String),
270 UnsupportedShape(String),
272 UnsupportedType(String),
274}
275
276impl core::fmt::Display for UrlEncodedError {
277 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
278 match self {
279 UrlEncodedError::InvalidNumber(field, value) => {
280 write!(f, "Invalid number for field '{}': '{}'", field, value)
281 }
282 UrlEncodedError::UnsupportedShape(shape) => {
283 write!(f, "Unsupported shape: {}", shape)
284 }
285 UrlEncodedError::UnsupportedType(ty) => {
286 write!(f, "Unsupported type: {}", ty)
287 }
288 }
289 }
290}
291
292impl std::error::Error for UrlEncodedError {}