#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use crate::convert::{try_percent_encode_iri_inline, MappedToUri};
use crate::spec::IriSpec;
#[cfg(feature = "alloc")]
use crate::task::ProcessAndWrite;
use crate::types::{
RiAbsoluteStr, RiFragmentStr, RiQueryStr, RiReferenceStr, RiRelativeStr, RiStr,
};
#[cfg(feature = "alloc")]
use crate::types::{
RiAbsoluteString, RiFragmentString, RiQueryString, RiReferenceString, RiRelativeString,
RiString,
};
use crate::types::{
UriAbsoluteStr, UriFragmentStr, UriQueryStr, UriReferenceStr, UriRelativeStr, UriStr,
};
#[cfg(feature = "alloc")]
use crate::types::{
UriAbsoluteString, UriFragmentString, UriQueryString, UriReferenceString, UriRelativeString,
UriString,
};
pub type IriAbsoluteStr = RiAbsoluteStr<IriSpec>;
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub type IriAbsoluteString = RiAbsoluteString<IriSpec>;
pub type IriFragmentStr = RiFragmentStr<IriSpec>;
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub type IriFragmentString = RiFragmentString<IriSpec>;
pub type IriStr = RiStr<IriSpec>;
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub type IriString = RiString<IriSpec>;
pub type IriReferenceStr = RiReferenceStr<IriSpec>;
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub type IriReferenceString = RiReferenceString<IriSpec>;
pub type IriRelativeStr = RiRelativeStr<IriSpec>;
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub type IriRelativeString = RiRelativeString<IriSpec>;
pub type IriQueryStr = RiQueryStr<IriSpec>;
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub type IriQueryString = RiQueryString<IriSpec>;
macro_rules! impl_conversion_between_uri {
(
$ty_owned_iri:ident,
$ty_owned_uri:ident,
$ty_borrowed_iri:ident,
$ty_borrowed_uri:ident,
$example_iri:expr,
$example_uri:expr
) => {
impl $ty_borrowed_iri {
#[doc = concat!("use iri_string::types::{", stringify!($ty_borrowed_iri), ", ", stringify!($ty_owned_uri), "};")]
#[doc = concat!("let iri = ", stringify!($ty_borrowed_iri), "::new(", stringify!($example_iri), ")?;")]
#[doc = concat!("let uri: ", stringify!($ty_owned_uri), " = iri.encode_to_uri();")]
#[doc = concat!("assert_eq!(uri, ", stringify!($example_uri), ");")]
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn encode_to_uri(&self) -> $ty_owned_uri {
MappedToUri::from(self).allocate_and_write().expect("failed to allocate memory")
}
#[doc = concat!("`", stringify!($ty_borrowed_uri), "::new(self.as_str()).ok()`.")]
#[doc = concat!("use iri_string::types::{", stringify!($ty_borrowed_iri), ", ", stringify!($ty_borrowed_uri), "};")]
#[doc = concat!("let ascii_iri = ", stringify!($ty_borrowed_iri), "::new(", stringify!($example_uri), ")?;")]
#[doc = concat!(" Some(", stringify!($example_uri), ")")]
#[doc = concat!("let nonascii_iri = ", stringify!($ty_borrowed_iri), "::new(", stringify!($example_iri), ")?;")]
pub fn as_uri(&self) -> Option<&$ty_borrowed_uri> {
if !self.as_str().is_ascii() {
return None;
}
debug_assert!(
<$ty_borrowed_uri>::new(self.as_str()).is_ok(),
"[consistency] the ASCII-only IRI must also be a valid URI"
);
let uri = unsafe {
<$ty_borrowed_uri>::new_maybe_unchecked(self.as_str())
};
Some(uri)
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl $ty_owned_iri {
#[doc = concat!("use iri_string::types::", stringify!($ty_owned_iri), ";")]
#[doc = concat!("let mut iri = ", stringify!($ty_owned_iri), "::try_from(", stringify!($example_iri), ")?;")]
#[doc = concat!("assert_eq!(iri, ", stringify!($example_uri), ");")]
pub fn encode_to_uri(&mut self) {
try_percent_encode_iri_inline(self.as_inner_mut())
.expect("failed to allocate memory");
debug_assert!(
<$ty_borrowed_iri>::new(self.as_str()).is_ok(),
"[consistency] the content must be valid at any time"
);
}
#[doc = concat!("use iri_string::types::{", stringify!($ty_owned_iri), ", ", stringify!($ty_owned_uri), "};")]
#[doc = concat!("let iri = ", stringify!($ty_owned_iri), "::try_from(", stringify!($example_iri), ")?;")]
#[doc = concat!("let uri: ", stringify!($ty_owned_uri), " = iri.encode_into_uri();")]
#[doc = concat!("assert_eq!(uri, ", stringify!($example_uri), ");")]
pub fn encode_into_uri(self) -> $ty_owned_uri {
MappedToUri::from(self.as_slice()).allocate_and_write().expect("failed to allocate memory")
}
#[doc = concat!("use iri_string::types::{", stringify!($ty_owned_iri), ", ", stringify!($ty_owned_uri), "};")]
#[doc = concat!("let ascii_iri = ", stringify!($ty_owned_iri), "::try_from(", stringify!($example_uri), ")?;")]
#[doc = concat!(" Ok(", stringify!($example_uri), ".to_string())")]
#[doc = concat!("let nonascii_iri = ", stringify!($ty_owned_iri), "::try_from(", stringify!($example_iri), ")?;")]
#[doc = concat!(" Err(", stringify!($example_iri), ".to_string())")]
pub fn try_into_uri(self) -> Result<$ty_owned_uri, $ty_owned_iri> {
if !self.as_str().is_ascii() {
return Err(self);
}
let s: String = self.into();
debug_assert!(
<$ty_borrowed_uri>::new(s.as_str()).is_ok(),
"[consistency] the ASCII-only IRI must also be a valid URI"
);
let uri = unsafe {
<$ty_owned_uri>::new_maybe_unchecked(s)
};
Ok(uri)
}
}
};
}
impl_conversion_between_uri!(
IriAbsoluteString,
UriAbsoluteString,
IriAbsoluteStr,
UriAbsoluteStr,
"http://example.com/?alpha=\u{03B1}",
"http://example.com/?alpha=%CE%B1"
);
impl_conversion_between_uri!(
IriReferenceString,
UriReferenceString,
IriReferenceStr,
UriReferenceStr,
"http://example.com/?alpha=\u{03B1}",
"http://example.com/?alpha=%CE%B1"
);
impl_conversion_between_uri!(
IriRelativeString,
UriRelativeString,
IriRelativeStr,
UriRelativeStr,
"../?alpha=\u{03B1}",
"../?alpha=%CE%B1"
);
impl_conversion_between_uri!(
IriString,
UriString,
IriStr,
UriStr,
"http://example.com/?alpha=\u{03B1}",
"http://example.com/?alpha=%CE%B1"
);
impl_conversion_between_uri!(
IriQueryString,
UriQueryString,
IriQueryStr,
UriQueryStr,
"alpha-is-\u{03B1}",
"alpha-is-%CE%B1"
);
impl_conversion_between_uri!(
IriFragmentString,
UriFragmentString,
IriFragmentStr,
UriFragmentStr,
"alpha-is-\u{03B1}",
"alpha-is-%CE%B1"
);
#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
use crate::types::{IriReferenceString, UriReferenceStr};
const CASES: &[(&str, &str)] = &[
("?alpha=\u{03B1}", "?alpha=%CE%B1"),
(
"?katakana-letter-i=\u{30A4}",
"?katakana-letter-i=%E3%82%A4",
),
("?sushi=\u{1f363}", "?sushi=%F0%9F%8D%A3"),
];
#[test]
fn iri_to_uri() {
for (iri, expected_uri) in CASES {
let expected_uri =
UriReferenceStr::new(*expected_uri).expect("test cases must be valid");
let mut iri = IriReferenceString::try_from(*iri).expect("test cases must be valid");
iri.encode_to_uri();
assert_eq!(iri, expected_uri);
iri.encode_to_uri();
assert_eq!(iri, expected_uri, "`encode_to_uri` must be idempotent");
}
}
}