use super::*;
use core::convert::TryFrom;
use core::fmt::Write;
use core::ops::Deref;
use core::str::FromStr;
use alloc::string::{String, ToString};
#[derive(Clone, Eq, Hash)]
pub struct UriBuf(pub(super) UriRefBuf);
impl_uri_buf_traits!(UriBuf, Uri);
impl Deref for UriBuf {
type Target = Uri;
fn deref(&self) -> &Self::Target {
self.as_uri()
}
}
impl AsRef<Uri> for UriBuf {
fn as_ref(&self) -> &Uri {
self.as_uri()
}
}
impl From<&Uri> for UriBuf {
fn from(x: &Uri) -> Self {
x.to_uri_buf()
}
}
impl FromStr for UriBuf {
type Err = ParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
Self::from_str(input)
}
}
impl TryFrom<&str> for UriBuf {
type Error = ParseError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::from_str(value)
}
}
impl TryFrom<String> for UriBuf {
type Error = ParseError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::from_string(value)
}
}
impl<'a> TryFrom<&'a String> for UriBuf {
type Error = <Self as TryFrom<&'a str>>::Error;
fn try_from(value: &'a String) -> Result<Self, Self::Error> {
<Self as TryFrom<&'a str>>::try_from(value.as_str())
}
}
impl core::fmt::Display for UriBuf {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.write_to(f)
}
}
impl UriBuf {
#[inline(always)]
pub unsafe fn from_string_unchecked(s: String) -> UriBuf {
UriBuf(UriRefBuf::from_string_unchecked(s))
}
}
impl UriBuf {
pub fn new<Sch, Hos, Pat, Que, Frg>(
scheme: Sch,
host: Hos,
port: Option<u16>,
path: Pat,
query: Option<Que>,
fragment: Option<Frg>,
) -> UriBuf
where
Sch: Into<String>,
Hos: AsRef<str>,
Pat: AsRef<str>,
Que: AsRef<str>,
Frg: AsRef<str>,
{
let mut ret: String = Self::from_scheme_host_port(scheme, host, port).into();
let mut path = path.as_ref();
if path.starts_with('/') {
path = &path[1..];
}
let path_segment_iter = path.split('/').filter(|seg| *seg != ".");
for seg in path_segment_iter {
ret.push('/');
ret.extend(seg.escape_uri());
}
if let Some(query) = query {
let mut first = true;
ret.push('?');
for seg in query.as_ref().split(|c| c == '&' || c == ';') {
if first {
first = false;
} else {
ret.push('&');
}
ret.extend(seg.escape_uri().for_query());
}
}
if let Some(fragment) = fragment {
ret.push('#');
ret.extend(fragment.as_ref().escape_uri().for_fragment());
}
unsafe { Self::from_string_unchecked(ret) }
}
pub fn from_scheme_authority<Sch, Aut>(scheme: Sch, authority: Aut) -> UriBuf
where
Sch: Into<String>,
Aut: AsRef<str>,
{
let mut ret = scheme.into();
assert_eq!(
ret.find(|c: char| !(c.is_ascii_alphanumeric() || c == '+' || c == '-' || c == '.')),
None,
"Scheme contains invalid characters: {:?}",
ret
);
ret.push_str("://");
ret.extend(authority.as_ref().escape_uri().for_authority());
unsafe { Self::from_string_unchecked(ret) }
}
pub fn from_host_rel_ref<Hos, RR>(host: Hos, rel_ref: RR) -> UriBuf
where
Hos: AsRef<str>,
RR: AsRef<RelRef>,
{
let host = host.as_ref();
let rel_ref = rel_ref
.as_ref()
.trim_leading_dot_slashes()
.trim_leading_slashes();
uri_format!("//{}/{}", host.escape_uri().full(), rel_ref).unwrap()
}
pub fn from_scheme_host_port<Sch, Hos>(scheme: Sch, host: Hos, port: Option<u16>) -> UriBuf
where
Sch: Into<String>,
Hos: AsRef<str>,
{
let mut ret = scheme.into();
assert_eq!(
ret.find(|c: char| !(c.is_ascii_alphanumeric() || c == '+' || c == '-' || c == '.')),
None,
"Scheme contains invalid characters: {:?}",
ret
);
ret.push_str("://");
let mut host = host.as_ref();
if host.starts_with('[') && host.ends_with(']') {
host = &host[1..host.len()];
}
if host.find(':').is_some() {
ret.push('[');
ret.extend(host.escape_uri());
ret.push(']');
}
if let Some(port) = port {
write!(ret, ":{}", port).unwrap();
}
unsafe { Self::from_string_unchecked(ret) }
}
pub fn from_str<S: AsRef<str>>(s: S) -> Result<UriBuf, ParseError> {
let s = s.as_ref();
let components = UriRawComponents::from_str(s)?;
if components.uri_type().can_borrow_as_uri() {
Ok(unsafe { Self::from_string_unchecked(s.to_string()) })
} else {
Err(ParseError::new("Missing scheme or authority", None))
}
}
pub fn from_string(s: String) -> Result<UriBuf, ParseError> {
let components = UriRawComponents::from_str(s.as_str())?;
if components.uri_type().can_borrow_as_uri() {
Ok(unsafe { Self::from_string_unchecked(s) })
} else {
Err(ParseError::new("Missing scheme or authority", None))
}
}
pub fn from_uri<S: AsRef<UriRef>>(s: S) -> Option<UriBuf> {
if s.as_ref().uri_type().can_borrow_as_uri() {
Some(UriBuf(s.as_ref().to_uri_ref_buf()))
} else {
None
}
}
}
impl UriBuf {
#[inline(always)]
pub fn as_uri(&self) -> &Uri {
unsafe { Uri::from_str_unchecked(self.as_str()) }
}
}
impl UriBuf {
pub fn resolve<T: AnyUriRef + ?Sized>(&mut self, dest: &T) -> Result<(), ResolveError> {
self.0.resolve(dest)
}
pub fn replace_path(&mut self, rel: &RelRef) {
self.0.replace_path(rel)
}
}
inherits_uri_ref_buf!(UriBuf);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_str() {
assert_eq!(
<UriBuf as FromStr>::from_str("https://www.google.com/"),
UriBuf::from_str("https://www.google.com/")
);
}
#[test]
fn test_try_from() {
assert_eq!(
UriBuf::try_from("https://www.google.com/"),
UriBuf::from_str("https://www.google.com/")
);
assert_eq!(
UriBuf::try_from("https://www.google.com/".to_string()),
UriBuf::from_str("https://www.google.com/")
);
assert_eq!(
UriBuf::try_from(&"https://www.google.com/".to_string()),
UriBuf::try_from("https://www.google.com/"),
);
}
}