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}