1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//!	# Percent encode fields.
//!
//!	This is a helper module for using serde_drive. It allows you to specify that
//!	some fields in your type will be automatically percent-encoded on
//!	serialization and percent-decoded on deserialization.
//!
//!	## Example
//!
//!	```
//!	use serde_derive::{Deserialize, Serialize};
//!
//!	#[derive(Debug, Deserialize, Serialize, PartialEq)]
//!	struct Item {
//!		#[serde(with="serde_urlencoded_field::path_segment")]
//!		id: String,
//!		mime: String,
//!	}
//!
//!	let item = Item {
//!		id: "my/item".to_string(),
//!		mime: "application/wasm".to_string(),
//!	};
//!
//!	let json = serde_json::to_string(&item).unwrap();
//!	assert_eq!(json, r#"{"id":"my%2Fitem","mime":"application/wasm"}"#);
//!
//!	let item2: Item = serde_json::from_str(&json).unwrap();
//!	assert_eq!(item2, item);
//!	```
//!
//!	## Types
//!
//!	serde_urlencoded_field can serialize anything that implements `AsRef<[u8]>`,
//!	including `String`, `&str`, `Vec<u8>` and `&[u8]`.
//!
//!	serde_urlencoded_field can deserialize into `String` and `Vec<u8>`. Other
//!	types can be added by implementing the FromPercentDecode trait.

#[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);