use bytes::BytesMut;
use conjure_object::{Plain, ToPlain};
use http::Uri;
use percent_encoding::{utf8_percent_encode, AsciiSet};
use std::collections::BTreeSet;
const QUERY: &AsciiSet = &percent_encoding::CONTROLS
.add(b' ')
.add(b'"')
.add(b'#')
.add(b'<')
.add(b'>');
const PATH: &AsciiSet = &QUERY.add(b'?').add(b'`').add(b'{').add(b'}');
const USERINFO: &AsciiSet = &PATH
.add(b'/')
.add(b':')
.add(b';')
.add(b'=')
.add(b'@')
.add(b'[')
.add(b'\\')
.add(b']')
.add(b'^')
.add(b'|');
const COMPONENT: &AsciiSet = &USERINFO.add(b'$').add(b'%').add(b'&').add(b'+').add(b',');
pub struct UriBuilder {
buf: BytesMut,
in_path: bool,
}
impl Default for UriBuilder {
fn default() -> Self {
Self::new()
}
}
impl UriBuilder {
pub fn new() -> Self {
UriBuilder {
buf: BytesMut::new(),
in_path: true,
}
}
pub fn push_literal(&mut self, components: &str) {
debug_assert!(components.starts_with('/'));
debug_assert!(!components.ends_with('/'));
debug_assert!(self.in_path);
self.buf.extend_from_slice(components.as_bytes());
}
pub fn push_path_parameter(&mut self, parameter: &dyn Plain) {
self.push_path_parameter_raw(¶meter.to_plain());
}
pub fn push_path_parameter_raw(&mut self, parameter: &str) {
debug_assert!(self.in_path);
self.buf.extend_from_slice(b"/");
self.push_escaped(parameter);
}
pub fn push_query_parameter(&mut self, key: &str, value: &dyn Plain) {
self.push_query_parameter_raw(key, &value.to_plain())
}
pub fn push_query_parameter_raw(&mut self, key: &str, value: &str) {
let prefix = if self.in_path { b"?" } else { b"&" };
self.in_path = false;
self.buf.extend_from_slice(prefix);
self.buf.extend_from_slice(key.as_bytes());
self.buf.extend_from_slice(b"=");
self.push_escaped(value);
}
pub fn push_optional_query_parameter<T>(&mut self, key: &str, value: &Option<T>)
where
T: Plain,
{
if let Some(value) = value {
self.push_query_parameter(key, value);
}
}
pub fn push_list_query_parameter<T>(&mut self, key: &str, values: &[T])
where
T: Plain,
{
for value in values {
self.push_query_parameter(key, value);
}
}
pub fn push_set_query_parameter<T>(&mut self, key: &str, values: &BTreeSet<T>)
where
T: Plain,
{
for value in values {
self.push_query_parameter(key, value);
}
}
fn push_escaped(&mut self, value: &str) {
for chunk in utf8_percent_encode(value, COMPONENT) {
self.buf.extend_from_slice(chunk.as_bytes());
}
}
pub fn build(self) -> Uri {
debug_assert!(!self.buf.is_empty());
Uri::from_maybe_shared(self.buf.freeze()).unwrap()
}
}