iri_string/types/generic/
fragment.rs

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