#[cfg(test)]mod test;
struct Visitor<T>(std::marker::PhantomData<T>);
impl<T> Visitor<T> {
fn new() -> Self {
Visitor(std::marker::PhantomData)
}
}
impl<'a, T: FromPercentDecode> serde::de::Visitor<'a> for Visitor<T> {
type Value = T;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a sequence of bytes")
}
fn visit_borrowed_str<E: serde::de::Error>(self, v: &'a str) -> Result<Self::Value, E> {
let decoded = percent_encoding::percent_decode(v.as_bytes());
FromPercentDecode::from(decoded)
}
}
pub trait FromPercentDecode: Sized {
fn from<E: serde::de::Error>(pd: percent_encoding::PercentDecode) -> Result<Self, E>;
}
impl FromPercentDecode for Vec<u8> {
fn from<E: serde::de::Error>(pd: percent_encoding::PercentDecode) -> Result<Self, E> {
Ok(pd.collect())
}
}
impl FromPercentDecode for String {
fn from<E: serde::de::Error>(pd: percent_encoding::PercentDecode) -> Result<Self, E> {
pd.decode_utf8()
.map(|cow| cow.into_owned())
.map_err(E::custom)
}
}
fn deserialize<'d, T: FromPercentDecode, D: serde::Deserializer<'d>>(
de: D,
) -> Result<T, D::Error> {
de.deserialize_str(Visitor::new())
}
fn serialize<S: serde::Serializer>(
data: impl AsRef<[u8]>,
ser: S,
encoding_set: &'static percent_encoding::AsciiSet,
) -> Result<S::Ok, S::Error> {
let encoded = percent_encoding::percent_encode(data.as_ref(), encoding_set)
.collect::<String>();
ser.serialize_str(&encoded)
}
macro_rules! version {
($name:ident, $set:path) => {
pub mod $name {
pub fn deserialize<'d, T: crate::FromPercentDecode, D: serde::Deserializer<'d>>(
de: D,
) -> Result<T, D::Error> {
crate::deserialize(de)
}
pub fn serialize<S: serde::Serializer>(
data: impl AsRef<[u8]>,
ser: S,
) -> Result<S::Ok, S::Error> {
crate::serialize(data, ser, &$set)
}
}
}
}
const QUERY_ENCODE_SET: percent_encoding::AsciiSet = percent_encoding::CONTROLS.add(b' ').add(b'"').add(b'#').add(b'<').add(b'>');
const DEFAULT_ENCODE_SET: percent_encoding::AsciiSet = QUERY_ENCODE_SET.add(b'`').add(b'?').add(b'{').add(b'}');
const PATH_SEGMENT_ENCODE_SET: percent_encoding::AsciiSet = DEFAULT_ENCODE_SET.add(b'%').add(b'/');
const USERINFO_ENCODE_SET: percent_encoding::AsciiSet = DEFAULT_ENCODE_SET.add(b'/').add(b':').add(b';').add(b'=').add(b'@').add(b'[').add(b'\\').add(b']').add(b'^').add(b'|');
version!(default, crate::DEFAULT_ENCODE_SET);
version!(path_segment, crate::PATH_SEGMENT_ENCODE_SET);
version!(query, crate::QUERY_ENCODE_SET);
version!(simple, percent_encoding::CONTROLS);
version!(userinfo, crate::USERINFO_ENCODE_SET);