iri_string/types/generic/fragment.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
//! Fragment string.
use crate::{
spec::Spec,
validate::{fragment, Error},
};
define_custom_string_slice! {
/// A borrowed slice of an IRI fragment (i.e. after the first `#` character).
///
/// This corresponds to [`ifragment` rule] in [RFC 3987] (and [`fragment` rule] in [RFC 3986]).
/// The rule for `ifragment` is `*( ipchar / "/" / "?" )`.
///
/// # Valid values
///
/// This type can have an IRI fragment.
/// Note that the IRI `foo://bar/baz#qux` has the fragment `qux`, **not** `#qux`.
///
/// ```
/// # use iri_string::types::IriFragmentStr;
/// assert!(IriFragmentStr::new("").is_ok());
/// assert!(IriFragmentStr::new("foo").is_ok());
/// assert!(IriFragmentStr::new("foo/bar").is_ok());
/// assert!(IriFragmentStr::new("/foo/bar").is_ok());
/// assert!(IriFragmentStr::new("//foo/bar").is_ok());
/// assert!(IriFragmentStr::new("https://user:pass@example.com:8080").is_ok());
/// assert!(IriFragmentStr::new("https://example.com/").is_ok());
/// ```
///
/// Some characters and sequences cannot used in a fragment.
///
/// ```
/// # use iri_string::types::IriFragmentStr;
/// // `<` and `>` cannot directly appear in an IRI reference.
/// assert!(IriFragmentStr::new("<not allowed>").is_err());
/// // Broken percent encoding cannot appear in an IRI reference.
/// assert!(IriFragmentStr::new("%").is_err());
/// assert!(IriFragmentStr::new("%GG").is_err());
/// // Hash sign `#` cannot appear in an IRI fragment.
/// assert!(IriFragmentStr::new("#hash").is_err());
/// ```
///
/// [RFC 3986]: https://tools.ietf.org/html/rfc3986
/// [RFC 3987]: https://tools.ietf.org/html/rfc3987
/// [`fragment` rule]: https://tools.ietf.org/html/rfc3986#section-3.5
/// [`ifragment` rule]: https://tools.ietf.org/html/rfc3987#section-2.2
struct RiFragmentStr {
validator = fragment,
expecting_msg = "IRI fragment string",
}
}
#[cfg(feature = "alloc")]
define_custom_string_owned! {
/// An owned string of an IRI fragment (i.e. after the first `#` character).
///
/// This corresponds to [`ifragment` rule] in [RFC 3987] (and [`fragment` rule] in [RFC 3986]).
/// The rule for `absolute-IRI` is `*( ipchar / "/" / "?" )`.
///
/// For details, see the documentation for [`RiFragmentStr`].
///
/// Enabled by `alloc` or `std` feature.
///
/// [RFC 3986]: https://tools.ietf.org/html/rfc3986
/// [RFC 3987]: https://tools.ietf.org/html/rfc3987
/// [`fragment` rule]: https://tools.ietf.org/html/rfc3986#section-3.5
/// [`ifragment` rule]: https://tools.ietf.org/html/rfc3987#section-2.2
/// [`RiFragmentStr`]: struct.RiFragmentStr.html
struct RiFragmentString {
validator = fragment,
slice = RiFragmentStr,
expecting_msg = "IRI fragment string",
}
}
impl<S: Spec> RiFragmentStr<S> {
/// Creates a new `&RiFragmentStr` from the fragment part prefixed by `#`.
///
/// # Examples
///
/// ```
/// # use iri_string::types::IriFragmentStr;
/// assert!(IriFragmentStr::from_prefixed("#").is_ok());
/// assert!(IriFragmentStr::from_prefixed("#foo").is_ok());
/// assert!(IriFragmentStr::from_prefixed("#foo/bar").is_ok());
/// assert!(IriFragmentStr::from_prefixed("#/foo/bar").is_ok());
/// assert!(IriFragmentStr::from_prefixed("#//foo/bar").is_ok());
/// assert!(IriFragmentStr::from_prefixed("#https://user:pass@example.com:8080").is_ok());
/// assert!(IriFragmentStr::from_prefixed("#https://example.com/").is_ok());
///
/// // `<` and `>` cannot directly appear in an IRI.
/// assert!(IriFragmentStr::from_prefixed("#<not allowed>").is_err());
/// // Broken percent encoding cannot appear in an IRI.
/// assert!(IriFragmentStr::new("#%").is_err());
/// assert!(IriFragmentStr::new("#%GG").is_err());
/// // `#` prefix is expected.
/// assert!(IriFragmentStr::from_prefixed("").is_err());
/// assert!(IriFragmentStr::from_prefixed("foo").is_err());
/// // Hash sign `#` cannot appear in an IRI fragment.
/// assert!(IriFragmentStr::from_prefixed("##hash").is_err());
/// ```
pub fn from_prefixed(s: &str) -> Result<&Self, Error> {
if !s.starts_with('#') {
return Err(Error::new());
}
TryFrom::try_from(&s[1..])
}
}