use super::*;
use core::convert::TryFrom;
use core::ops::Deref;
use core::str::FromStr;
use alloc::string::{String, ToString};
#[derive(Clone, Eq, Hash)]
pub struct UriRefBuf(pub(super) String);
_impl_uri_buf_traits_base!(UriRefBuf, UriRef);
impl FromStr for UriRefBuf {
type Err = ParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
Self::from_str(input)
}
}
impl TryFrom<&str> for UriRefBuf {
type Error = ParseError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::from_str(value)
}
}
impl TryFrom<String> for UriRefBuf {
type Error = ParseError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::from_string(value)
}
}
impl<'a> TryFrom<&'a String> for UriRefBuf {
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 Default for UriRefBuf {
fn default() -> Self {
Self::new()
}
}
impl Deref for UriRefBuf {
type Target = UriRef;
fn deref(&self) -> &Self::Target {
self.as_uri_ref()
}
}
impl AsRef<String> for UriRefBuf {
fn as_ref(&self) -> &String {
&self.0
}
}
impl core::fmt::Display for UriRefBuf {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.write_to(f)
}
}
impl UriRefBuf {
pub fn new() -> UriRefBuf {
UriRefBuf(String::new())
}
pub fn with_capacity(capacity: usize) -> UriRefBuf {
UriRefBuf(String::with_capacity(capacity))
}
pub fn from_str<S: AsRef<str> + Copy>(s: S) -> Result<Self, ParseError> {
let str_ref = s.as_ref();
UriRef::from_str(str_ref)?;
Ok(UriRefBuf(str_ref.to_string()))
}
pub fn from_string(s: String) -> Result<Self, ParseError> {
UriRef::from_str(s.as_str())?;
Ok(UriRefBuf(s))
}
}
impl UriRefBuf {
#[inline(always)]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
#[inline(always)]
pub fn as_uri_ref(&self) -> &UriRef {
unsafe { UriRef::from_str_unchecked(self.as_str()) }
}
#[inline(always)]
pub fn as_mut_uri_ref(&mut self) -> &mut UriRef {
unsafe { UriRef::from_str_unchecked_mut(self.as_mut_str()) }
}
}
impl UriRefBuf {
pub fn truncate_fragment(&mut self) {
if let Some(i) = self.fragment_start() {
self.0.truncate(i)
}
}
pub fn truncate_heir_part(&mut self) {
self.0.truncate(self.heir_part_start())
}
pub fn truncate_query(&mut self) {
if let Some(i) = self.query_start().or_else(|| self.fragment_start()) {
self.0.truncate(i)
}
}
pub fn truncate_path(&mut self) {
self.0.truncate(self.path_start());
}
pub fn truncate_resource(&mut self) {
self.truncate_query();
let path_start = self.as_uri_ref().path_start();
if let Some(i) = self.as_str().rfind('/') {
if i + 1 > path_start {
self.0.truncate(i + 1);
}
} else if path_start == 0 {
self.clear();
}
}
pub fn truncate_last_path_segment(&mut self) {
let path_start = self.as_uri_ref().path_start();
let path_end = self.as_uri_ref().path_end();
if path_start == path_end {
return;
}
self.truncate_query();
let mut s = self.as_str();
if s.ends_with('/') {
s = &s[..s.len() - 1];
}
if let Some(i) = s.rfind('/') {
if i + 1 > path_start {
self.0.truncate(i + 1);
}
} else if path_start == 0 {
self.clear();
}
if self.raw_path().is_empty() {
self.0.push_str("./");
}
}
pub fn clear(&mut self) {
self.0.clear()
}
pub fn add_trailing_slash(&mut self) -> bool {
if self.is_empty() {
self.0.push_str("./");
true
} else {
let path_end = self.path_end();
if path_end > 0 && &self[path_end - 1..path_end] == "/" {
false
} else {
self.0.insert(path_end, '/');
true
}
}
}
pub fn add_leading_slash(&mut self) -> bool {
let path_begin = self.path_start();
if self.len() > 0 && &self[path_begin..path_begin + 1] == "/" {
false
} else {
self.0.insert(path_begin, '/');
true
}
}
pub fn resolve<T: AnyUriRef + ?Sized>(&mut self, dest: &T) -> Result<(), ResolveError> {
if !dest.is_empty() {
*self = self.resolved(dest)?;
}
Ok(())
}
pub fn push_path_segment(&mut self, segment: &str, trailing_slash: bool) {
if segment == "." {
if trailing_slash {
self.add_trailing_slash();
} else if self.is_empty() {
self.0.push_str(".");
}
return;
}
if segment == ".." {
self.truncate_last_path_segment();
return;
}
self.truncate_query();
if !self.is_empty() {
self.add_trailing_slash();
}
self.0.extend(segment.escape_uri());
if trailing_slash {
self.add_trailing_slash();
}
}
pub fn push_query_item(&mut self, item: &str) {
self.truncate_fragment();
if self.query_start().is_some() {
self.0.push('&');
} else {
self.0.push('?');
}
self.0.extend(item.escape_uri().for_query());
}
pub fn push_query_key_value(&mut self, key: &str, value: &str) {
self.truncate_fragment();
if self.query_start().is_some() {
self.0.push('&');
} else {
self.0.push('?');
}
self.0.extend(key.escape_uri().for_query());
self.0.push('=');
self.0.extend(value.escape_uri().for_query());
}
pub fn replace_path(&mut self, rel: &RelRef) {
self.truncate_path();
if !rel.starts_with(|c| c == '/' || c == '?' || c == '#') {
self.add_trailing_slash();
}
self.0.push_str(rel.as_str());
}
}
impl UriRefBuf {
pub unsafe fn from_string_unchecked(s: String) -> UriRefBuf {
UriRefBuf(s)
}
#[inline(always)]
pub unsafe fn as_mut_str(&mut self) -> &mut str {
self.0.as_mut_str()
}
pub unsafe fn as_mut_string_ref(&mut self) -> &mut String {
&mut self.0
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! inherits_uri_ref_buf {
($BUF:ident) => {
impl $BUF {
#[inline(always)]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
#[inline(always)]
pub unsafe fn as_mut_str(&mut self) -> &mut str {
self.0.as_mut_str()
}
#[inline(always)]
pub fn as_mut_uri_ref(&mut self) -> &mut UriRef {
self.0.as_mut_uri_ref()
}
#[inline(always)]
pub fn truncate_heir_part(&mut self) {
self.0.truncate_heir_part()
}
#[inline(always)]
pub fn truncate_path(&mut self) {
self.0.truncate_path()
}
#[inline(always)]
pub fn truncate_query(&mut self) {
self.0.truncate_query()
}
#[inline(always)]
pub fn truncate_fragment(&mut self) {
self.0.truncate_fragment()
}
#[inline(always)]
pub fn truncate_resource(&mut self) {
self.0.truncate_resource()
}
#[inline(always)]
pub fn truncate_last_path_segment(&mut self) {
self.0.truncate_last_path_segment()
}
#[inline(always)]
pub fn add_trailing_slash(&mut self) -> bool {
self.0.add_trailing_slash()
}
#[inline(always)]
pub fn add_leading_slash(&mut self) -> bool {
self.0.add_leading_slash()
}
#[inline(always)]
pub fn push_path_segment(&mut self, segment: &str, trailing_slash: bool) {
self.0.push_path_segment(segment, trailing_slash)
}
#[inline(always)]
pub fn push_query_item(&mut self, item: &str) {
self.0.push_query_item(item)
}
#[inline(always)]
pub fn push_query_key_value(&mut self, key: &str, value: &str) {
self.0.push_query_key_value(key, value)
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_try_from() {
assert_eq!(
UriRefBuf::try_from("https://www.google.com/"),
UriRefBuf::from_str("https://www.google.com/")
);
assert_eq!(
UriRefBuf::try_from("https://www.google.com/".to_string()),
UriRefBuf::from_str("https://www.google.com/")
);
assert_eq!(
UriRefBuf::try_from(&"https://www.google.com/".to_string()),
UriRefBuf::try_from("https://www.google.com/"),
);
}
#[test]
fn test_from_str() {
assert_eq!(
<UriRefBuf as FromStr>::from_str("https://www.google.com/"),
UriRefBuf::from_str("https://www.google.com/")
);
assert!(UriRefBuf::from_str("http://example.com/").is_ok());
}
#[test]
fn push_path_segment() {
let mut uri = iuri_ref!("").to_uri_ref_buf();
uri.push_path_segment(".", false);
assert_eq!(uri, iuri_ref!("."));
let mut uri = iuri_ref!("").to_uri_ref_buf();
uri.push_path_segment("foobar", false);
assert_eq!(uri, iuri_ref!("foobar"));
uri.push_path_segment("a/b/c", true);
assert_eq!(uri, iuri_ref!("foobar/a%2Fb%2Fc/"));
uri.push_path_segment(".", true);
assert_eq!(uri, iuri_ref!("foobar/a%2Fb%2Fc/"));
uri.push_path_segment("..", false);
assert_eq!(uri, iuri_ref!("foobar/"));
uri.push_path_segment("..", false);
assert_eq!(uri, iuri_ref!("./"));
}
#[test]
fn add_trailing_slash() {
let mut uri = iuri_ref!("example/").to_uri_ref_buf();
assert_eq!(false, uri.add_trailing_slash());
let mut uri = iuri_ref!("example").to_uri_ref_buf();
assert_eq!(true, uri.add_trailing_slash());
assert_eq!(iuri_ref!("example/"), &uri);
let mut uri = iuri_ref!("example?").to_uri_ref_buf();
assert_eq!(true, uri.add_trailing_slash());
assert_eq!(iuri_ref!("example/?"), &uri);
let mut uri = iuri_ref!("example#").to_uri_ref_buf();
assert_eq!(true, uri.add_trailing_slash());
assert_eq!(iuri_ref!("example/#"), &uri);
let mut uri = iuri_ref!("example?/#/").to_uri_ref_buf();
assert_eq!(true, uri.add_trailing_slash());
assert_eq!(iuri_ref!("example/?/#/"), &uri);
let mut uri = iuri_ref!("/e/x/a/m/p/l/e?/#/").to_uri_ref_buf();
assert_eq!(true, uri.add_trailing_slash());
assert_eq!(iuri_ref!("/e/x/a/m/p/l/e/?/#/"), &uri);
}
#[test]
fn add_leading_slash() {
let mut uri = iuri_ref!("/example").to_uri_ref_buf();
assert_eq!(false, uri.add_leading_slash());
let mut uri = iuri_ref!("example").to_uri_ref_buf();
assert_eq!(true, uri.add_leading_slash());
assert_eq!(iuri_ref!("/example"), &uri);
let mut uri = iuri_ref!("example?").to_uri_ref_buf();
assert_eq!(true, uri.add_leading_slash());
assert_eq!(iuri_ref!("/example?"), &uri);
let mut uri = iuri_ref!("example#").to_uri_ref_buf();
assert_eq!(true, uri.add_leading_slash());
assert_eq!(iuri_ref!("/example#"), &uri);
let mut uri = iuri_ref!("example?/#/").to_uri_ref_buf();
assert_eq!(true, uri.add_leading_slash());
assert_eq!(iuri_ref!("/example?/#/"), &uri);
let mut uri = iuri_ref!("e/x/a/m/p/l/e/?/#/").to_uri_ref_buf();
assert_eq!(true, uri.add_leading_slash());
assert_eq!(iuri_ref!("/e/x/a/m/p/l/e/?/#/"), &uri);
}
}