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}