use std::borrow::Cow;
use std::fmt::{self, Display};
use percent_encoding::{AsciiSet, CONTROLS, utf8_percent_encode};
use url::Url;
pub(crate) const FRAGMENT: &AsciiSet = &CONTROLS
.add(b':')
.add(b'?')
.add(b'#')
.add(b'[')
.add(b']')
.add(b'@')
.add(b'!')
.add(b'$')
.add(b'&')
.add(b'\'')
.add(b'(')
.add(b')')
.add(b'*')
.add(b'+')
.add(b',')
.add(b';')
.add(b'=')
.add(b'"')
.add(b' ')
.add(b'<')
.add(b'>')
.add(b'%')
.add(b'{')
.add(b'}')
.add(b'|')
.add(b'\\')
.add(b'^')
.add(b'`');
pub(crate) const FRAGMENT_SLASH: &AsciiSet = &FRAGMENT.add(b'/');
pub(crate) fn percent_encode(val: &str) -> impl Display + Into<Cow<'_, str>> + '_ {
utf8_percent_encode(val, FRAGMENT_SLASH)
}
pub(crate) fn percent_encode_path(val: &str) -> impl Display + Into<Cow<'_, str>> + '_ {
utf8_percent_encode(val, FRAGMENT)
}
pub(crate) fn canonical_query_string<'a, Q>(
query_string: Q,
mut out: impl fmt::Write,
) -> fmt::Result
where
Q: Iterator<Item = (&'a str, &'a str)>,
{
let mut first = true;
for (key, val) in query_string {
if first {
first = false;
} else {
out.write_char('&')?;
}
write!(out, "{}={}", percent_encode(key), percent_encode(val))?;
}
Ok(())
}
pub(crate) fn add_query_params<'a, Q>(mut url: Url, params: Q) -> Url
where
Q: Iterator<Item = (&'a str, &'a str)>,
{
let mut query = String::new();
canonical_query_string(params, &mut query).expect("String writer panicked");
if !query.is_empty() {
match url.query() {
Some(existing) if !existing.is_empty() => {
url.set_query(Some(&format!("{existing}&{query}")));
}
_ => url.set_query(Some(&query)),
}
}
url
}