use {
hyper::{
Uri,
header::{HeaderName, HeaderValue},
},
percent_encoding::{AsciiSet, CONTROLS, percent_decode_str, utf8_percent_encode},
std::{
any::type_name,
collections::HashMap,
io::{Error as IoError, Result as IoResult},
str::FromStr,
},
};
const QUERY_ENCODE_SET: &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'~');
pub fn get_path<T>() -> String {
let name = type_name::<T>();
let name = if let Some(i) = name.find("<") {
&name[..i]
} else {
name
};
format!(
"/{}",
name.split("::").last().unwrap_or("").replace("_", "/")
)
}
pub fn url_decode(encoded: &str) -> Result<String, String> {
percent_decode_str(&encoded.replace('+', "%20"))
.decode_utf8()
.map(|cow| cow.into_owned())
.map_err(|e| e.to_string())
}
pub fn build_uri<P>(base_url: &Uri, path: P, force_scheme: Option<&str>) -> IoResult<Uri>
where
P: AsRef<str>,
{
let is_tls = matches!(base_url.scheme_str(), Some("https" | "wss"));
let scheme = if let Some(forced) = force_scheme {
if is_tls && !forced.ends_with('s') {
format!("{}s", forced)
} else {
forced.to_owned()
}
} else {
base_url.scheme_str().unwrap_or_default().to_owned()
};
let default_port = if is_tls { 443 } else { 80 };
let base = format!(
"{}://{}:{}{}",
scheme,
base_url.host().unwrap_or_default(),
base_url.port_u16().unwrap_or(default_port),
base_url.path()
);
let url = if base.ends_with('/') {
format!("{}{}", base.trim_end_matches('/'), path.as_ref())
} else {
format!("{}{}", base, path.as_ref())
};
let full_url = format!(
"{}{}",
url,
base_url
.query()
.map(|q| format!("?{}", q))
.unwrap_or_default()
);
full_url.parse().map_err(IoError::other)
}
pub fn append_query_params(uri: &Uri, params: &HashMap<String, String>) -> Uri {
if params.is_empty() {
return uri.clone();
}
let query_string: String = params
.iter()
.map(|(k, v)| {
format!(
"{}={}",
utf8_percent_encode(k, QUERY_ENCODE_SET),
utf8_percent_encode(v, QUERY_ENCODE_SET)
)
})
.collect::<Vec<_>>()
.join("&");
let existing_query = uri.query().unwrap_or("");
let new_query = if existing_query.is_empty() {
query_string
} else {
format!("{}&{}", existing_query, query_string)
};
let new_uri = format!(
"{}://{}{}?{}",
uri.scheme_str().unwrap_or("http"),
uri.authority().map(|a| a.as_str()).unwrap_or("localhost"),
uri.path(),
new_query
);
new_uri.parse().unwrap_or_else(|_| uri.clone())
}
pub fn parse_header(name: &str, value: &str) -> Result<(HeaderName, HeaderValue), IoError> {
let name = HeaderName::from_str(name).map_err(IoError::other)?;
let value = HeaderValue::from_str(value).map_err(IoError::other)?;
Ok((name, value))
}