#![warn(missing_docs)]
#![doc = include_str!("../README.md")]
use shapely::{Partial, error, trace, warn};
#[cfg(test)]
mod tests;
pub fn from_urlencoded(partial: &mut Partial, input: &str) -> Result<(), UrlEncodedError> {
use shapely::{Innards, Scalar};
trace!("Starting URL encoded form data deserialization");
let pairs = form_urlencoded::parse(input.as_bytes());
let mut pairs_map: std::collections::HashMap<String, String> = std::collections::HashMap::new();
for (key, value) in pairs {
pairs_map.insert(key.to_string(), value.to_string());
}
let shape_desc = partial.shape();
let shape = shape_desc.get();
trace!("Deserializing value with shape:\n{:?}", shape);
match &shape.innards {
Innards::Struct { .. } => {
trace!("Deserializing \x1b[1;36mstruct\x1b[0m");
for (key, value) in pairs_map {
trace!("Processing field key: \x1b[1;33m{}\x1b[0m", key);
let slot = match partial.slot_by_name(&key) {
Ok(slot) => slot,
Err(_) => {
warn!("Unknown field: {}", key);
continue; }
};
let slot_shape = slot.shape();
let slot_shape_ref = slot_shape.get();
match &slot_shape_ref.innards {
Innards::Scalar(scalar) => {
let mut partial_field = Partial::alloc(slot.shape());
let field_slot = partial_field.scalar_slot().expect("Scalar slot");
match scalar {
Scalar::String => {
field_slot.fill(value);
}
Scalar::U64 => match value.parse::<u64>() {
Ok(num) => field_slot.fill(num),
Err(_) => {
return Err(UrlEncodedError::InvalidNumber(key.clone(), value));
}
},
_ => {
warn!("Unsupported scalar type: {:?}", scalar);
return Err(UrlEncodedError::UnsupportedType(format!(
"{:?}",
scalar
)));
}
}
slot.fill_from_partial(partial_field);
}
_ => {
error!("Unsupported shape: {:?}", slot_shape_ref.innards);
return Err(UrlEncodedError::UnsupportedShape(format!(
"{:?}",
slot_shape_ref.innards
)));
}
}
}
trace!("Finished deserializing \x1b[1;36mstruct\x1b[0m");
}
_ => {
error!("Unsupported shape: {:?}", shape.innards);
return Err(UrlEncodedError::UnsupportedShape(format!(
"{:?}",
shape.innards
)));
}
}
trace!(
"Successfully deserialized URL encoded form data for shape: \x1b[1;32m{}\x1b[0m at address \x1b[1;34m{:?}\x1b[0m\n",
shape,
partial.addr()
);
Ok(())
}
#[derive(Debug)]
pub enum UrlEncodedError {
InvalidNumber(String, String),
UnsupportedShape(String),
UnsupportedType(String),
}
impl std::fmt::Display for UrlEncodedError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UrlEncodedError::InvalidNumber(field, value) => {
write!(f, "Invalid number for field '{}': '{}'", field, value)
}
UrlEncodedError::UnsupportedShape(shape) => {
write!(f, "Unsupported shape: {}", shape)
}
UrlEncodedError::UnsupportedType(ty) => {
write!(f, "Unsupported type: {}", ty)
}
}
}
}
impl std::error::Error for UrlEncodedError {}