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