use std::borrow::Cow;
use std::io::{self, Write};
use std::str::from_utf8_unchecked;
use crate::percent_encoding::{utf8_percent_encode, AsciiSet};
pub use percent_encoding::CONTROLS;
pub use percent_encoding::NON_ALPHANUMERIC;
pub const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
pub const QUERY: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'#').add(b'<').add(b'>');
pub const SPECIAL_QUERY: &AsciiSet = &QUERY.add(b'\'');
pub const PATH: &AsciiSet = &QUERY.add(b'?').add(b'`').add(b'{').add(b'}');
pub const USERINFO: &AsciiSet = &PATH
.add(b'/')
.add(b':')
.add(b';')
.add(b'=')
.add(b'@')
.add(b'[')
.add(b'\\')
.add(b']')
.add(b'^')
.add(b'|');
pub const COMPONENT: &AsciiSet = &USERINFO.add(b'$').add(b'%').add(b'&').add(b'+').add(b',');
pub const X_WWW_FORM_URLENCODED: &AsciiSet =
&COMPONENT.add(b'!').add(b'\'').add(b'(').add(b')').add(b'~');
#[inline]
pub fn encode<'a, S: ?Sized + AsRef<str>>(
text: &'a S,
ascii_set: &'static AsciiSet,
) -> Cow<'a, str> {
Cow::from(utf8_percent_encode(text.as_ref(), ascii_set))
}
#[inline]
pub fn encode_to_string<'a, S: AsRef<str>>(
text: S,
ascii_set: &'static AsciiSet,
output: &'a mut String,
) -> &'a str {
unsafe { from_utf8_unchecked(encode_to_vec(text, ascii_set, output.as_mut_vec())) }
}
pub fn encode_to_vec<'a, S: AsRef<str>>(
text: S,
ascii_set: &'static AsciiSet,
output: &'a mut Vec<u8>,
) -> &'a [u8] {
let text = text.as_ref();
let text_bytes = text.as_bytes();
let text_length = text_bytes.len();
output.reserve(text_length);
let current_length = output.len();
let pe = utf8_percent_encode(text, ascii_set);
output.extend(pe.flat_map(|e| e.bytes()));
&output[current_length..]
}
#[inline]
pub fn encode_to_writer<S: AsRef<str>, W: Write>(
text: S,
ascii_set: &'static AsciiSet,
output: &mut W,
) -> Result<(), io::Error> {
let pe = utf8_percent_encode(text.as_ref(), ascii_set);
for s in pe {
output.write_all(s.as_bytes())?;
}
Ok(())
}
macro_rules! encode_impl {
($(#[$attr: meta])* $escape_set:ident; $(#[$encode_attr: meta])* $encode_name: ident; $(#[$encode_to_string_attr: meta])* $encode_to_string_name: ident; $(#[$encode_to_vec_attr: meta])* $encode_to_vec_name: ident; $(#[$encode_to_writer_attr: meta])* $encode_to_writer_name: ident $(;)*) => {
$(#[$encode_attr])*
$(#[$attr])*
#[inline]
pub fn $encode_name<S: ?Sized + AsRef<str>>(text: &S) -> Cow<str> {
encode(text, $escape_set)
}
$(#[$encode_to_string_attr])*
$(#[$attr])*
#[inline]
pub fn $encode_to_string_name<S: AsRef<str>>(text: S, output: &mut String) -> &str {
encode_to_string(text, $escape_set, output)
}
$(#[$encode_to_vec_attr])*
$(#[$attr])*
#[inline]
pub fn $encode_to_vec_name<S: AsRef<str>>(text: S, output: &mut Vec<u8>) -> &[u8] {
encode_to_vec(text, $escape_set, output)
}
$(#[$encode_to_writer_attr])*
$(#[$attr])*
#[inline]
pub fn $encode_to_writer_name<S: AsRef<str>, W: Write>(text: S, output: &mut W) -> Result<(), io::Error> {
encode_to_writer(text, $escape_set, output)
}
};
}
encode_impl! {
FRAGMENT;
encode_fragment;
encode_fragment_to_string;
encode_fragment_to_vec;
encode_fragment_to_writer;
}
encode_impl! {
QUERY;
encode_query;
encode_query_to_string;
encode_query_to_vec;
encode_query_to_writer;
}
encode_impl! {
SPECIAL_QUERY;
encode_special_query;
encode_special_query_to_string;
encode_special_query_to_vec;
encode_special_query_to_writer;
}
encode_impl! {
PATH;
encode_path;
encode_path_to_string;
encode_path_to_vec;
encode_path_to_writer;
}
encode_impl! {
USERINFO;
encode_userinfo;
encode_userinfo_to_string;
encode_userinfo_to_vec;
encode_userinfo_to_writer;
}
encode_impl! {
COMPONENT;
encode_component;
encode_component_to_string;
encode_component_to_vec;
encode_component_to_writer;
}
encode_impl! {
X_WWW_FORM_URLENCODED;
encode_www_form_urlencoded;
encode_www_form_urlencoded_to_string;
encode_www_form_urlencoded_to_vec;
encode_www_form_urlencoded_to_writer;
}