use core::fmt;
use serde::de::Error;
#[derive(Debug)]
pub enum BadUrlEncodedCharacter {
BadlyFormattedPercentEncoding,
Utf8Error,
}
impl fmt::Display for BadUrlEncodedCharacter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BadlyFormattedPercentEncoding => {
write!(f, "Percent symbol is not followed by two hex digits")
}
Self::Utf8Error => write!(
f,
"Percent-encoded sequence does not decode into UTF-8 byte sequence"
),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for BadUrlEncodedCharacter {}
pub enum UrlDecodedCharacter {
Literal(char),
Encoded(char),
}
impl UrlDecodedCharacter {
pub fn into_char(self) -> char {
match self {
UrlDecodedCharacter::Literal(c) | UrlDecodedCharacter::Encoded(c) => c,
}
}
}
pub struct UrlDecodedCharacters<'a>(core::str::Chars<'a>);
impl<'a> UrlDecodedCharacters<'a> {
pub fn as_str(&self) -> UrlEncodedString<'a> {
UrlEncodedString(self.0.as_str())
}
}
impl<'a> Iterator for UrlDecodedCharacters<'a> {
type Item = Result<UrlDecodedCharacter, BadUrlEncodedCharacter>;
fn next(&mut self) -> Option<Self::Item> {
Some(Ok(match self.0.next()? {
'+' => UrlDecodedCharacter::Encoded(' '),
'%' => {
fn to_hex(c: char) -> Option<u8> {
c.to_digit(16).map(|b| b as u8)
}
struct Ones(u8);
impl Iterator for Ones {
type Item = ();
fn next(&mut self) -> Option<Self::Item> {
let b = (0b10000000 & self.0) > 0;
self.0 <<= 1;
b.then_some(())
}
}
let mut first_byte = {
let Some(first) = self.0.next().and_then(to_hex) else {
return Some(Err(BadUrlEncodedCharacter::BadlyFormattedPercentEncoding));
};
let Some(second) = self.0.next().and_then(to_hex) else {
return Some(Err(BadUrlEncodedCharacter::BadlyFormattedPercentEncoding));
};
first * 0x10 + second
};
let mut bits = Ones(first_byte);
let code_point = if bits.next().is_some() {
let byte_count = 1 + bits.count();
if byte_count == 1 {
return Some(Err(BadUrlEncodedCharacter::Utf8Error));
}
first_byte <<= byte_count;
first_byte >>= byte_count;
let mut code_point = u32::from(first_byte);
for _ in 1..byte_count {
let Some('%') = self.0.next() else {
return Some(Err(BadUrlEncodedCharacter::Utf8Error));
};
let next_byte = {
let Some(first) = self.0.next().and_then(to_hex) else {
return Some(Err(
BadUrlEncodedCharacter::BadlyFormattedPercentEncoding,
));
};
let Some(second) = self.0.next().and_then(to_hex) else {
return Some(Err(
BadUrlEncodedCharacter::BadlyFormattedPercentEncoding,
));
};
first * 0x10 + second
};
if (0b11000000 & next_byte) != 0b10000000 {
return Some(Err(BadUrlEncodedCharacter::Utf8Error));
}
code_point <<= 6;
code_point += u32::from(0b00111111 & next_byte);
}
code_point
} else {
first_byte.into()
};
let Some(c) = char::from_u32(code_point) else {
return Some(Err(BadUrlEncodedCharacter::Utf8Error));
};
UrlDecodedCharacter::Encoded(c)
}
c => UrlDecodedCharacter::Literal(c),
}))
}
}
const URL_ENCODED_KEY: &str = "____URL_ENCODED____";
fn deserializer<'a>(
key: &'a str,
value: UrlEncodedString<'a>,
) -> serde::de::value::MapDeserializer<
'a,
core::option::IntoIter<(&'a str, DeserializeUrlEncoded<'a>)>,
DeserializationError,
> {
serde::de::value::MapDeserializer::new(
Some((URL_ENCODED_KEY, DeserializeUrlEncoded { key, value })).into_iter(),
)
}
#[derive(serde::Deserialize)]
struct UrlEncodedRepresentation<'a> {
#[serde(rename = "____URL_ENCODED____")]
value: &'a str,
}
#[derive(Debug)]
pub enum DecodeError {
BadUrlEncodedCharacter(BadUrlEncodedCharacter),
NoSpace,
}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BadUrlEncodedCharacter(bad_url_encoded_character) => {
bad_url_encoded_character.fmt(f)
}
Self::NoSpace => write!(f, "No space to decode url-encoded string"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DecodeError {}
struct NamedDecodeError<'a> {
key: &'a str,
error: DecodeError,
}
#[derive(Clone, Copy, serde::Deserialize)]
#[serde(from = "UrlEncodedRepresentation")]
pub struct UrlEncodedString<'a>(pub &'a str);
impl<'a> fmt::Debug for UrlEncodedString<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<'r> PartialEq<&'r str> for UrlEncodedString<'r> {
fn eq(&self, other: &&'r str) -> bool {
matches!(self.strip_prefix(other), Some(UrlEncodedString("")))
}
}
impl<'de> From<UrlEncodedRepresentation<'de>> for UrlEncodedString<'de> {
fn from(UrlEncodedRepresentation { value }: UrlEncodedRepresentation<'de>) -> Self {
Self(value)
}
}
impl<'a> UrlEncodedString<'a> {
pub fn chars(self) -> UrlDecodedCharacters<'a> {
UrlDecodedCharacters(self.0.chars())
}
pub fn try_into_string<const N: usize>(self) -> Result<heapless::String<N>, DecodeError> {
let mut str = heapless::String::new();
for c in self.chars() {
str.push(c.map_err(DecodeError::BadUrlEncodedCharacter)?.into_char())
.map_err(|()| DecodeError::NoSpace)?;
}
Ok(str)
}
#[cfg(feature = "std")]
pub fn try_into_std_string(self) -> Result<std::string::String, BadUrlEncodedCharacter> {
self.chars()
.map(|c| c.map(UrlDecodedCharacter::into_char))
.collect()
}
pub fn strip_prefix(self, prefix: &str) -> Option<Self> {
let mut chars = self.chars();
for c in prefix.chars() {
if c == '/' {
let UrlDecodedCharacter::Literal('/') = chars.next()?.ok()? else {
return None;
};
} else if c != chars.next()?.ok()?.into_char() {
return None;
}
}
Some(chars.as_str())
}
pub fn is_empty(self) -> bool {
self.0.is_empty()
}
fn with_decoded<'d, T, E: From<NamedDecodeError<'d>>, F: FnOnce(&str) -> Result<T, E>>(
self,
key: &'d str,
f: F,
) -> Result<T, E> {
f(&self
.try_into_string::<1024>()
.map_err(|error| NamedDecodeError { key, error })?)
}
}
#[derive(Debug)]
pub(crate) struct DeserializationError;
impl fmt::Display for DeserializationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Deserialization Error")
}
}
impl serde::de::Error for DeserializationError {
fn custom<T: fmt::Display>(msg: T) -> Self {
#[cfg(feature = "std")]
println!("DeserializationError: {msg}");
#[cfg(not(feature = "std"))]
drop(msg);
Self
}
}
#[cfg(feature = "std")]
impl std::error::Error for DeserializationError {}
impl<'de> From<NamedDecodeError<'de>> for DeserializationError {
fn from(NamedDecodeError { key, error }: NamedDecodeError) -> Self {
Self::custom(format_args!("No space to decode {key}: {error}"))
}
}
struct DeserializeUrlEncoded<'de> {
pub key: &'de str,
pub value: UrlEncodedString<'de>,
}
impl<'de> serde::de::IntoDeserializer<'de, DeserializationError> for DeserializeUrlEncoded<'de> {
type Deserializer = Self;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
macro_rules! deserialize_parse_value {
($this:ident, $key_value:expr; $($deserialize:ident $visit:ident)*) => {
$(
fn $deserialize<V: serde::de::Visitor<'de>>(
$this,
visitor: V,
) -> Result<V::Value, Self::Error> {
let (key, value) = $key_value;
value.with_decoded(key, |value| {
visitor.$visit(value.parse().map_err(|err| {
DeserializationError::custom(format_args!("Failed to parse {}: {}", key, err))
})?)
})
}
)*
};
}
impl<'de> serde::Deserializer<'de> for DeserializeUrlEncoded<'de> {
type Error = DeserializationError;
fn deserialize_any<V: serde::de::Visitor<'de>>(
self,
visitor: V,
) -> Result<V::Value, Self::Error> {
self.value.with_decoded(self.key, |v| visitor.visit_str(v))
}
fn deserialize_struct<V: serde::de::Visitor<'de>>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error> {
if name == "UrlEncodedString" && fields == [URL_ENCODED_KEY] {
deserializer(self.key, self.value).deserialize_struct(name, fields, visitor)
} else {
Err(DeserializationError::custom("paths items must be atomic"))
}
}
fn deserialize_enum<V: serde::de::Visitor<'de>>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error> {
self.value.with_decoded(self.key, |value| {
visitor.visit_enum(serde::de::value::StrDeserializer::new(value))
})
}
fn deserialize_option<V: serde::de::Visitor<'de>>(
self,
visitor: V,
) -> Result<V::Value, Self::Error> {
visitor.visit_some(self)
}
deserialize_parse_value!(
self, (self.key, self.value);
deserialize_bool visit_bool
deserialize_f32 visit_f32 deserialize_f64 visit_f64
deserialize_i8 visit_i8 deserialize_i16 visit_i16 deserialize_i32 visit_i32 deserialize_i64 visit_i64
deserialize_u8 visit_u8 deserialize_u16 visit_u16 deserialize_u32 visit_u32 deserialize_u64 visit_u64
);
serde::forward_to_deserialize_any! {
char str string
bytes byte_buf unit unit_struct newtype_struct seq tuple
tuple_struct map identifier ignored_any
}
}
#[derive(Debug)]
pub struct BadUrlEncodedForm;
impl fmt::Display for BadUrlEncodedForm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Bad Url Encoded Form")
}
}
impl serde::de::Error for BadUrlEncodedForm {
fn custom<T: fmt::Display>(_msg: T) -> Self {
Self
}
}
#[cfg(feature = "std")]
impl std::error::Error for BadUrlEncodedForm {}
impl From<super::url_encoded::DeserializationError> for BadUrlEncodedForm {
fn from(
super::url_encoded::DeserializationError: super::url_encoded::DeserializationError,
) -> Self {
Self
}
}
struct DeserializeUrlEncodedForm<'r> {
pairs: core::str::Split<'r, char>,
value: (&'r str, UrlEncodedString<'r>),
}
pub fn deserialize_url_encoded_form<T: serde::de::DeserializeOwned>(
form: &str,
) -> Result<T, BadUrlEncodedForm> {
T::deserialize(DeserializeUrlEncodedForm {
pairs: form.split('&'),
value: ("", UrlEncodedString("")),
})
}
impl<'de> serde::de::Deserializer<'de> for DeserializeUrlEncodedForm<'de> {
type Error = BadUrlEncodedForm;
fn deserialize_any<V: serde::de::Visitor<'de>>(
self,
visitor: V,
) -> Result<V::Value, Self::Error> {
visitor.visit_map(self)
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
}
impl<'de> serde::de::MapAccess<'de> for DeserializeUrlEncodedForm<'de> {
type Error = BadUrlEncodedForm;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
where
K: serde::de::DeserializeSeed<'de>,
{
self.pairs
.next()
.map(|value| {
let (key, value) = value.split_once('=').ok_or(BadUrlEncodedForm)?;
self.value = (key, UrlEncodedString(value));
Ok(seed.deserialize(DeserializeUrlEncoded {
key,
value: UrlEncodedString(key),
})?)
})
.transpose()
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: serde::de::DeserializeSeed<'de>,
{
let (name, value) = self.value;
Ok(seed.deserialize(DeserializeUrlEncoded { key: name, value })?)
}
}