use core::convert::TryFrom;
use core::fmt;
#[cfg(feature = "alloc")]
use alloc::borrow::Cow;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::rc::Rc;
#[cfg(feature = "alloc")]
use alloc::sync::Arc;
use crate::spec::Spec;
use crate::template::context::Context;
use crate::template::error::{Error, ErrorKind};
use crate::template::expand::Expanded;
use crate::template::parser::validate_template_str;
#[cfg(feature = "alloc")]
pub use self::owned::UriTemplateString;
macro_rules! impl_cmp {
($ty_common:ty, $ty_lhs:ty, $ty_rhs:ty) => {
impl PartialEq<$ty_rhs> for $ty_lhs {
#[inline]
fn eq(&self, o: &$ty_rhs) -> bool {
<$ty_common as PartialEq<$ty_common>>::eq(self.as_ref(), o.as_ref())
}
}
impl PartialEq<$ty_lhs> for $ty_rhs {
#[inline]
fn eq(&self, o: &$ty_lhs) -> bool {
<$ty_common as PartialEq<$ty_common>>::eq(self.as_ref(), o.as_ref())
}
}
impl PartialOrd<$ty_rhs> for $ty_lhs {
#[inline]
fn partial_cmp(&self, o: &$ty_rhs) -> Option<core::cmp::Ordering> {
<$ty_common as PartialOrd<$ty_common>>::partial_cmp(self.as_ref(), o.as_ref())
}
}
impl PartialOrd<$ty_lhs> for $ty_rhs {
#[inline]
fn partial_cmp(&self, o: &$ty_lhs) -> Option<core::cmp::Ordering> {
<$ty_common as PartialOrd<$ty_common>>::partial_cmp(self.as_ref(), o.as_ref())
}
}
};
}
#[cfg(feature = "alloc")]
mod owned;
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UriTemplateStr {
inner: str,
}
impl UriTemplateStr {
#[inline]
pub fn new(s: &str) -> Result<&Self, Error> {
TryFrom::try_from(s)
}
#[inline]
#[must_use]
unsafe fn new_always_unchecked(s: &str) -> &Self {
&*(s as *const str as *const Self)
}
#[inline]
#[must_use]
pub fn as_str(&self) -> &str {
self.as_ref()
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.as_str().len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.as_str().is_empty()
}
}
impl UriTemplateStr {
#[inline]
pub fn expand<'a, S: Spec, C: Context>(
&'a self,
context: &'a C,
) -> Result<Expanded<'a, S, C>, Error> {
Expanded::new(self, context)
}
}
impl fmt::Debug for UriTemplateStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("UriTemplateStr").field(&&self.inner).finish()
}
}
impl AsRef<str> for UriTemplateStr {
#[inline]
fn as_ref(&self) -> &str {
&self.inner
}
}
impl AsRef<UriTemplateStr> for UriTemplateStr {
#[inline]
fn as_ref(&self) -> &UriTemplateStr {
self
}
}
#[cfg(feature = "alloc")]
impl<'a> From<&'a UriTemplateStr> for Cow<'a, UriTemplateStr> {
#[inline]
fn from(s: &'a UriTemplateStr) -> Self {
Cow::Borrowed(s)
}
}
#[cfg(feature = "alloc")]
impl From<&UriTemplateStr> for Arc<UriTemplateStr> {
fn from(s: &UriTemplateStr) -> Self {
let inner: &str = s.as_str();
let buf = Arc::<str>::from(inner);
unsafe {
let raw: *const str = Arc::into_raw(buf);
Self::from_raw(raw as *const UriTemplateStr)
}
}
}
#[cfg(feature = "alloc")]
impl From<&UriTemplateStr> for Box<UriTemplateStr> {
fn from(s: &UriTemplateStr) -> Self {
let inner: &str = s.as_str();
let buf = Box::<str>::from(inner);
unsafe {
let raw: *mut str = Box::into_raw(buf);
Self::from_raw(raw as *mut UriTemplateStr)
}
}
}
#[cfg(feature = "alloc")]
impl From<&UriTemplateStr> for Rc<UriTemplateStr> {
fn from(s: &UriTemplateStr) -> Self {
let inner: &str = s.as_str();
let buf = Rc::<str>::from(inner);
unsafe {
let raw: *const str = Rc::into_raw(buf);
Self::from_raw(raw as *const UriTemplateStr)
}
}
}
impl<'a> From<&'a UriTemplateStr> for &'a str {
#[inline]
fn from(s: &'a UriTemplateStr) -> &'a str {
s.as_ref()
}
}
impl<'a> TryFrom<&'a str> for &'a UriTemplateStr {
type Error = Error;
#[inline]
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
match validate_template_str(s) {
Ok(()) => Ok(unsafe { UriTemplateStr::new_always_unchecked(s) }),
Err(e) => Err(e),
}
}
}
impl<'a> TryFrom<&'a [u8]> for &'a UriTemplateStr {
type Error = Error;
#[inline]
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
let s = core::str::from_utf8(bytes)
.map_err(|e| Error::new(ErrorKind::InvalidUtf8, e.valid_up_to()))?;
match validate_template_str(s) {
Ok(()) => Ok(unsafe { UriTemplateStr::new_always_unchecked(s) }),
Err(e) => Err(e),
}
}
}
impl_cmp!(str, str, UriTemplateStr);
impl_cmp!(str, &str, UriTemplateStr);
impl_cmp!(str, str, &UriTemplateStr);
impl fmt::Display for UriTemplateStr {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(feature = "serde")]
mod __serde_slice {
use super::UriTemplateStr;
use core::{convert::TryFrom, fmt};
use serde::{
de::{self, Visitor},
Deserialize, Deserializer,
};
#[derive(Debug, Clone, Copy)]
struct CustomStrVisitor;
impl<'de> Visitor<'de> for CustomStrVisitor {
type Value = &'de UriTemplateStr;
#[inline]
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("URI template string")
}
#[inline]
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: de::Error,
{
<&'de UriTemplateStr as TryFrom<&'de str>>::try_from(v).map_err(E::custom)
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'a, 'de: 'a> Deserialize<'de> for &'a UriTemplateStr {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_string(CustomStrVisitor)
}
}
}