pub struct PercentEncoded(String);
impl PercentEncoded {
pub fn new(value: &str) -> Self {
Self(urlencoding::encode(value).into_owned())
}
#[inline]
pub const fn new_unchecked(value: String) -> Self {
Self(value)
}
#[inline]
pub const fn uri_component(&self) -> &String {
&self.0
}
pub fn uri_component_retaining_sep(&self) -> String {
self.0.replace("%2F", "/")
}
pub fn display(&self) -> String {
urlencoding::decode(&self.0)
.expect("The same value can be decoded and encoded")
.into_owned()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encoding() {
let cases = vec![
("", ""),
("AAAAAAAAAA", "AAAAAAAAAA"),
("AAAAA.AAAAA", "AAAAA.AAAAA"),
("foo$bar", "foo%24bar"),
("foo%bar", "foo%25bar"),
("foo&bar", "foo%26bar"),
("/", "%2F"),
("foo/bar", "foo%2Fbar"),
("foo/$bar", "foo%2F%24bar"),
("foo/%bar", "foo%2F%25bar"),
("foo/&bar", "foo%2F%26bar"),
("/foo/bar", "%2Ffoo%2Fbar"),
("foo/bar/baz", "foo%2Fbar%2Fbaz"),
("/foo/bar/baz", "%2Ffoo%2Fbar%2Fbaz"),
];
for (value, expected) in cases {
let actual = PercentEncoded::new(value);
assert_eq!(actual.uri_component(), expected);
}
}
#[test]
fn test_encoding_retaining_separator() {
let cases = vec![
("", ""),
("AAAAAAAAAA", "AAAAAAAAAA"),
("AAAAA.AAAAA", "AAAAA.AAAAA"),
("foo$bar", "foo%24bar"),
("foo%bar", "foo%25bar"),
("foo&bar", "foo%26bar"),
("/", "/"),
("foo/bar", "foo/bar"),
("foo/$bar", "foo/%24bar"),
("foo/%bar", "foo/%25bar"),
("foo/&bar", "foo/%26bar"),
("/foo/bar", "/foo/bar"),
("foo/bar/baz", "foo/bar/baz"),
("/foo/bar/baz", "/foo/bar/baz"),
];
for (value, expected) in cases {
let actual = PercentEncoded::new(value);
assert_eq!(actual.uri_component_retaining_sep(), expected);
}
}
#[test]
fn test_decoding() {
let cases = vec![
("", ""),
("AAAAAAAAAA", "AAAAAAAAAA"),
("AAAAA.AAAAA", "AAAAA.AAAAA"),
("foo%24bar", "foo$bar"),
("foo%25bar", "foo%bar"),
("foo%26bar", "foo&bar"),
("%2F", "/"),
("foo%2Fbar", "foo/bar"),
("foo%2F%24bar", "foo/$bar"),
("foo%2F%25bar", "foo/%bar"),
("foo%2F%26bar", "foo/&bar"),
("%2Ffoo%2Fbar", "/foo/bar"),
("foo%2Fbar%2Fbaz", "foo/bar/baz"),
("%2Ffoo%2Fbar%2Fbaz", "/foo/bar/baz"),
];
for (value, expected) in cases {
let actual = PercentEncoded::new_unchecked(value.to_string());
assert_eq!(actual.display(), expected);
}
}
}