Last active
August 28, 2017 10:51
-
-
Save lo48576/c0285251edc851eeea367f3a4c114b00 to your computer and use it in GitHub Desktop.
IRI in Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! IRI types. | |
// This implementation is based on `str`, `String`, `std::str` and `std::path` | |
// on rust-1.19.0 . | |
// See: | |
// | |
// * https://github.com/rust-lang/rust/blob/1.19.0/src/libcore/str/mod.rs | |
// * https://github.com/rust-lang/rust/blob/1.19.0/src/libcollections/string.rs | |
// * https://github.com/rust-lang/rust/blob/1.19.0/src/libstd/path.rs | |
use std::borrow::{Borrow, ToOwned, Cow}; | |
use std::cmp; | |
use std::ops::Deref; | |
use std::error; | |
use std::fmt; | |
use url; // extern crate. | |
/// An IRI slice. | |
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
pub struct Iri { | |
inner: str, | |
} | |
impl Iri { | |
/// Converts a string slice to an IRI slice without checking that the string contains valid | |
/// IRI. | |
#[inline] | |
pub unsafe fn from_str_unchecked(s: &str) -> &Self { | |
::std::mem::transmute(s) | |
} | |
/// Converts a string slice to an IRI slice. | |
pub fn from_str(s: &str) -> Result<(&Self, url::Url), ParseError> { | |
let url = run_iri_validation(s)?; | |
let iri = unsafe { Self::from_str_unchecked(s) }; | |
Ok((iri, url)) | |
} | |
/// Converts an `Iri` to an owned `IriBuf`. | |
#[inline] | |
pub fn to_iri_buf(&self) -> IriBuf { | |
IriBuf { inner: self.inner.to_owned() } | |
} | |
/// Converts an `Iri` to an `url::Url`. | |
pub fn to_url(&self) -> url::Url { | |
url::Url::parse(self).expect( | |
"Failed to convert an `Iri` to an `Url::url` (should never happen)", | |
) | |
} | |
} | |
impl AsRef<str> for Iri { | |
fn as_ref(&self) -> &str { | |
self | |
} | |
} | |
impl AsRef<Iri> for Iri { | |
fn as_ref(&self) -> &Iri { | |
self | |
} | |
} | |
impl Deref for Iri { | |
type Target = str; | |
fn deref(&self) -> &Self::Target { | |
&self.inner | |
} | |
} | |
impl ToOwned for Iri { | |
type Owned = IriBuf; | |
fn to_owned(&self) -> Self::Owned { | |
self.to_iri_buf() | |
} | |
} | |
impl<'a> From<&'a Iri> for url::Url { | |
fn from(v: &Iri) -> Self { | |
v.to_url() | |
} | |
} | |
/// An owned IRI. | |
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
pub struct IriBuf { | |
inner: String, | |
} | |
impl IriBuf { | |
/// Converts a string to an owned IRI without checking that the string contains valid IRI. | |
#[inline] | |
pub unsafe fn from_string_unchecked(s: String) -> Self { | |
IriBuf { inner: s } | |
} | |
/// Converts a string to an owned IRI. | |
pub fn from_string(s: String) -> Result<(Self, url::Url), ParseError> { | |
let url = run_iri_validation(&s)?; | |
let iri = Self { inner: s }; | |
Ok((iri, url)) | |
} | |
/// Coerces to an `Iri` slice. | |
#[inline] | |
pub fn as_iri(&self) -> &Iri { | |
unsafe { Iri::from_str_unchecked(self.inner.as_str()) } | |
} | |
/// Converts an `IriBuf` to an `url::Url`. | |
pub fn to_url(&self) -> url::Url { | |
url::Url::parse(self).expect( | |
"Failed to convert an `IriBuf` to an `Url::url` (should never happen)", | |
) | |
} | |
} | |
impl AsRef<str> for IriBuf { | |
fn as_ref(&self) -> &str { | |
self | |
} | |
} | |
impl AsRef<Iri> for IriBuf { | |
fn as_ref(&self) -> &Iri { | |
self | |
} | |
} | |
impl Borrow<Iri> for IriBuf { | |
fn borrow(&self) -> &Iri { | |
self.as_iri() | |
} | |
} | |
impl Deref for IriBuf { | |
type Target = Iri; | |
fn deref(&self) -> &Self::Target { | |
self.as_iri() | |
} | |
} | |
impl ::std::str::FromStr for IriBuf { | |
type Err = ParseError; | |
fn from_str(s: &str) -> Result<Self, Self::Err> { | |
Iri::from_str(s).map(|(iri, _)| iri).map(ToOwned::to_owned) | |
} | |
} | |
impl From<IriBuf> for url::Url { | |
fn from(v: IriBuf) -> Self { | |
v.to_url() | |
} | |
} | |
// Based on https://github.com/rust-lang/rust/blob/1.19.0/src/libstd/path.rs#L2424-L2460 | |
macro_rules! impl_cmp { | |
($lhs:ty, $rhs:ty) => { | |
impl<'a, 'b> PartialEq<$rhs> for $lhs { | |
#[inline] | |
fn eq(&self, other: &$rhs) -> bool { <Iri as PartialEq>::eq(self, other) } | |
#[inline] | |
fn ne(&self, other: &$rhs) -> bool { <Iri as PartialEq>::ne(self, other) } | |
} | |
impl<'a, 'b> PartialEq<$lhs> for $rhs { | |
#[inline] | |
fn eq(&self, other: &$lhs) -> bool { <Iri as PartialEq>::eq(self, other) } | |
#[inline] | |
fn ne(&self, other: &$lhs) -> bool { <Iri as PartialEq>::ne(self, other) } | |
} | |
impl<'a, 'b> PartialOrd<$rhs> for $lhs { | |
#[inline] | |
fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> { | |
<Iri as PartialOrd>::partial_cmp(self, other) | |
} | |
} | |
impl<'a, 'b> PartialOrd<$lhs> for $rhs { | |
#[inline] | |
fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> { | |
<Iri as PartialOrd>::partial_cmp(self, other) | |
} | |
} | |
}; | |
} | |
impl_cmp!(IriBuf, Iri); | |
impl_cmp!(IriBuf, &'a Iri); | |
impl_cmp!(Cow<'a, Iri>, Iri); | |
impl_cmp!(Cow<'a, Iri>, &'b Iri); | |
impl_cmp!(Cow<'a, Iri>, IriBuf); | |
/// An IRI parse error. | |
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | |
pub enum ParseError { | |
/// Parse error from the `url` crate. | |
Url(url::ParseError), | |
} | |
impl fmt::Display for ParseError { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
let ParseError::Url(ref e) = *self; | |
write!(f, "Provided string was invalid as IRI: {}", e) | |
} | |
} | |
impl error::Error for ParseError { | |
fn description(&self) -> &str { | |
let ParseError::Url(ref e) = *self; | |
error::Error::description(e) | |
} | |
} | |
impl From<url::ParseError> for ParseError { | |
fn from(v: url::ParseError) -> Self { | |
ParseError::Url(v) | |
} | |
} | |
/// Checks whether the given string is valid IRI. | |
fn run_iri_validation(s: &str) -> Result<url::Url, ParseError> { | |
Ok(url::Url::parse(s)?) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment