iri_string/types/generic/
fragment.rs

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