iri_string/validate.rs
1//! Validators.
2//!
3//! Validators are functions that receive the string and checks if the entire
4//! string is syntactically valid.
5
6use core::fmt;
7
8#[cfg(feature = "std")]
9use std::error;
10
11use crate::parser::validate as parser;
12use crate::spec::Spec;
13
14/// Resource identifier validation error.
15// Note that this type should implement `Copy` trait.
16// To return additional non-`Copy` data as an error, use wrapper type
17// (as `std::string::FromUtf8Error` contains `std::str::Utf8Error`).
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct Error {
20 /// Error kind.
21 kind: ErrorKind,
22}
23
24impl Error {
25 /// Creates a new `Error` from the given error kind.
26 #[inline]
27 #[must_use]
28 pub(crate) fn with_kind(kind: ErrorKind) -> Self {
29 Self { kind }
30 }
31}
32
33impl fmt::Display for Error {
34 #[inline]
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 write!(f, "invalid IRI: {}", self.kind.description())
37 }
38}
39
40#[cfg(feature = "std")]
41impl error::Error for Error {}
42
43/// Error kind.
44///
45/// This type may be reorganized between minor version bumps, so users should
46/// not expect specific error kind (or specific error message) to be returned
47/// for a specific error.
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49#[non_exhaustive]
50pub(crate) enum ErrorKind {
51 /// Invalid scheme.
52 InvalidScheme,
53 /// Invalid userinfo.
54 InvalidUserInfo,
55 /// Invalid host.
56 InvalidHost,
57 /// Invalid port.
58 InvalidPort,
59 /// Invalid path character.
60 InvalidPath,
61 /// Invalid query.
62 InvalidQuery,
63 /// Invalid fragment.
64 InvalidFragment,
65 /// Got an unexpected fragment.
66 UnexpectedFragment,
67 /// Expected a relative IRI but got an absolute IRI.
68 UnexpectedAbsolute,
69 /// Expected an absolute IRI but got a relative IRI.
70 UnexpectedRelative,
71 /// Invalid UTF-8 bytes.
72 InvalidUtf8,
73}
74
75impl ErrorKind {
76 /// Returns the human-friendly description for the error kind.
77 #[must_use]
78 fn description(self) -> &'static str {
79 match self {
80 Self::InvalidScheme => "invalid scheme",
81 Self::InvalidUserInfo => "invalid userinfo",
82 Self::InvalidHost => "invalid host",
83 Self::InvalidPort => "invalid port",
84 Self::InvalidPath => "invalid path",
85 Self::InvalidQuery => "invalid query",
86 Self::InvalidFragment => "invalid fragment",
87 Self::UnexpectedFragment => "unexpected fragment",
88 Self::UnexpectedAbsolute => "expected a relative IRI but got an absolute IRI",
89 Self::UnexpectedRelative => "expected an absolute IRI but got a relative IRI",
90 Self::InvalidUtf8 => "invalid utf-8 bytes",
91 }
92 }
93}
94
95/// Validates [IRI][uri].
96///
97/// This validator corresponds to [`RiStr`] and [`RiString`] types.
98///
99/// # Examples
100///
101/// This type can have an IRI (which is absolute, and may have fragment part).
102///
103/// ```
104/// use iri_string::{spec::UriSpec, validate::iri};
105/// assert!(iri::<UriSpec>("https://user:pass@example.com:8080").is_ok());
106/// assert!(iri::<UriSpec>("https://example.com/").is_ok());
107/// assert!(iri::<UriSpec>("https://example.com/foo?bar=baz").is_ok());
108/// assert!(iri::<UriSpec>("https://example.com/foo?bar=baz#qux").is_ok());
109/// assert!(iri::<UriSpec>("foo:bar").is_ok());
110/// assert!(iri::<UriSpec>("foo:").is_ok());
111/// // `foo://.../` below are all allowed. See the crate documentation for detail.
112/// assert!(iri::<UriSpec>("foo:/").is_ok());
113/// assert!(iri::<UriSpec>("foo://").is_ok());
114/// assert!(iri::<UriSpec>("foo:///").is_ok());
115/// assert!(iri::<UriSpec>("foo:////").is_ok());
116/// assert!(iri::<UriSpec>("foo://///").is_ok());
117/// ```
118///
119/// Relative IRI reference is not allowed.
120///
121/// ```
122/// use iri_string::{spec::UriSpec, validate::iri};
123/// // This is relative path.
124/// assert!(iri::<UriSpec>("foo/bar").is_err());
125/// // `/foo/bar` is an absolute path, but it is authority-relative.
126/// assert!(iri::<UriSpec>("/foo/bar").is_err());
127/// // `//foo/bar` is termed "network-path reference",
128/// // or usually called "protocol-relative reference".
129/// assert!(iri::<UriSpec>("//foo/bar").is_err());
130/// // Same-document reference is relative.
131/// assert!(iri::<UriSpec>("#foo").is_err());
132/// // Empty string is not a valid absolute IRI.
133/// assert!(iri::<UriSpec>("").is_err());
134/// ```
135///
136/// Some characters and sequences cannot used in an IRI.
137///
138/// ```
139/// use iri_string::{spec::UriSpec, validate::iri};
140/// // `<` and `>` cannot directly appear in an IRI.
141/// assert!(iri::<UriSpec>("<not allowed>").is_err());
142/// // Broken percent encoding cannot appear in an IRI.
143/// assert!(iri::<UriSpec>("%").is_err());
144/// assert!(iri::<UriSpec>("%GG").is_err());
145/// ```
146///
147/// [uri]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3
148/// [`RiStr`]: ../types/struct.RiStr.html
149/// [`RiString`]: ../types/struct.RiString.html
150pub fn iri<S: Spec>(s: &str) -> Result<(), Error> {
151 parser::validate_uri::<S>(s)
152}
153
154/// Validates [IRI reference][uri-reference].
155///
156/// This validator corresponds to [`RiReferenceStr`] and [`RiReferenceString`] types.
157///
158/// # Examples
159///
160/// This type can have an IRI reference (which can be absolute or relative).
161///
162/// ```
163/// use iri_string::{spec::UriSpec, validate::iri_reference};
164/// assert!(iri_reference::<UriSpec>("https://user:pass@example.com:8080").is_ok());
165/// assert!(iri_reference::<UriSpec>("https://example.com/").is_ok());
166/// assert!(iri_reference::<UriSpec>("https://example.com/foo?bar=baz").is_ok());
167/// assert!(iri_reference::<UriSpec>("https://example.com/foo?bar=baz#qux").is_ok());
168/// assert!(iri_reference::<UriSpec>("foo:bar").is_ok());
169/// assert!(iri_reference::<UriSpec>("foo:").is_ok());
170/// // `foo://.../` below are all allowed. See the crate documentation for detail.
171/// assert!(iri_reference::<UriSpec>("foo:/").is_ok());
172/// assert!(iri_reference::<UriSpec>("foo://").is_ok());
173/// assert!(iri_reference::<UriSpec>("foo:///").is_ok());
174/// assert!(iri_reference::<UriSpec>("foo:////").is_ok());
175/// assert!(iri_reference::<UriSpec>("foo://///").is_ok());
176/// assert!(iri_reference::<UriSpec>("foo/bar").is_ok());
177/// assert!(iri_reference::<UriSpec>("/foo/bar").is_ok());
178/// assert!(iri_reference::<UriSpec>("//foo/bar").is_ok());
179/// assert!(iri_reference::<UriSpec>("#foo").is_ok());
180/// ```
181///
182/// Some characters and sequences cannot used in an IRI reference.
183///
184/// ```
185/// use iri_string::{spec::UriSpec, validate::iri_reference};
186/// // `<` and `>` cannot directly appear in an IRI reference.
187/// assert!(iri_reference::<UriSpec>("<not allowed>").is_err());
188/// // Broken percent encoding cannot appear in an IRI reference.
189/// assert!(iri_reference::<UriSpec>("%").is_err());
190/// assert!(iri_reference::<UriSpec>("%GG").is_err());
191/// ```
192///
193/// [uri-reference]: https://www.rfc-editor.org/rfc/rfc3986.html#section-4.1
194/// [`RiReferenceStr`]: ../types/struct.RiReferenceStr.html
195/// [`RiReferenceString`]: ../types/struct.RiReferenceString.html
196pub fn iri_reference<S: Spec>(s: &str) -> Result<(), Error> {
197 parser::validate_uri_reference::<S>(s)
198}
199
200/// Validates [absolute IRI][absolute-uri].
201///
202/// This validator corresponds to [`RiAbsoluteStr`] and [`RiAbsoluteString`] types.
203///
204/// # Examples
205///
206/// This type can have an absolute IRI without fragment part.
207///
208/// ```
209/// use iri_string::{spec::UriSpec, validate::absolute_iri};
210/// assert!(absolute_iri::<UriSpec>("https://example.com/foo?bar=baz").is_ok());
211/// assert!(absolute_iri::<UriSpec>("foo:bar").is_ok());
212/// // Scheme `foo` and empty path.
213/// assert!(absolute_iri::<UriSpec>("foo:").is_ok());
214/// // `foo://.../` below are all allowed. See the crate documentation for detail.
215/// assert!(absolute_iri::<UriSpec>("foo:/").is_ok());
216/// assert!(absolute_iri::<UriSpec>("foo://").is_ok());
217/// assert!(absolute_iri::<UriSpec>("foo:///").is_ok());
218/// assert!(absolute_iri::<UriSpec>("foo:////").is_ok());
219/// assert!(absolute_iri::<UriSpec>("foo://///").is_ok());
220///
221/// ```
222///
223/// Relative IRI is not allowed.
224///
225/// ```
226/// use iri_string::{spec::UriSpec, validate::absolute_iri};
227/// // This is relative path.
228/// assert!(absolute_iri::<UriSpec>("foo/bar").is_err());
229/// // `/foo/bar` is an absolute path, but it is authority-relative.
230/// assert!(absolute_iri::<UriSpec>("/foo/bar").is_err());
231/// // `//foo/bar` is termed "network-path reference",
232/// // or usually called "protocol-relative reference".
233/// assert!(absolute_iri::<UriSpec>("//foo/bar").is_err());
234/// // Empty string is not a valid absolute IRI.
235/// assert!(absolute_iri::<UriSpec>("").is_err());
236/// ```
237///
238/// Fragment part (such as trailing `#foo`) is not allowed.
239///
240/// ```
241/// use iri_string::{spec::UriSpec, validate::absolute_iri};
242/// // Fragment part is not allowed.
243/// assert!(absolute_iri::<UriSpec>("https://example.com/foo?bar=baz#qux").is_err());
244/// ```
245///
246/// Some characters and sequences cannot used in an absolute IRI.
247///
248/// ```
249/// use iri_string::{spec::UriSpec, validate::absolute_iri};
250/// // `<` and `>` cannot directly appear in an absolute IRI.
251/// assert!(absolute_iri::<UriSpec>("<not allowed>").is_err());
252/// // Broken percent encoding cannot appear in an absolute IRI.
253/// assert!(absolute_iri::<UriSpec>("%").is_err());
254/// assert!(absolute_iri::<UriSpec>("%GG").is_err());
255/// ```
256///
257/// [absolute-uri]: https://www.rfc-editor.org/rfc/rfc3986.html#section-4.3
258/// [`RiAbsoluteStr`]: ../types/struct.RiAbsoluteStr.html
259/// [`RiAbsoluteString`]: ../types/struct.RiAbsoluteString.html
260pub fn absolute_iri<S: Spec>(s: &str) -> Result<(), Error> {
261 parser::validate_absolute_uri::<S>(s)
262}
263
264/// Validates [relative reference][relative-ref].
265///
266/// This validator corresponds to [`RiRelativeStr`] and [`RiRelativeString`] types.
267///
268/// # Valid values
269///
270/// This type can have a relative IRI reference.
271///
272/// ```
273/// use iri_string::{spec::UriSpec, validate::relative_ref};
274/// assert!(relative_ref::<UriSpec>("foo").is_ok());
275/// assert!(relative_ref::<UriSpec>("foo/bar").is_ok());
276/// assert!(relative_ref::<UriSpec>("/foo").is_ok());
277/// assert!(relative_ref::<UriSpec>("//foo/bar").is_ok());
278/// assert!(relative_ref::<UriSpec>("?foo").is_ok());
279/// assert!(relative_ref::<UriSpec>("#foo").is_ok());
280/// assert!(relative_ref::<UriSpec>("foo/bar?baz#qux").is_ok());
281/// // The first path component can have colon if the path is absolute.
282/// assert!(relative_ref::<UriSpec>("/foo:bar/").is_ok());
283/// // Second or following path components can have colon.
284/// assert!(relative_ref::<UriSpec>("foo/bar://baz/").is_ok());
285/// assert!(relative_ref::<UriSpec>("./foo://bar").is_ok());
286/// ```
287///
288/// Absolute form of a reference is not allowed.
289///
290/// ```
291/// use iri_string::{spec::UriSpec, validate::relative_ref};
292/// assert!(relative_ref::<UriSpec>("https://example.com/").is_err());
293/// // The first path component cannot have colon, if the path is not absolute.
294/// assert!(relative_ref::<UriSpec>("foo:bar").is_err());
295/// assert!(relative_ref::<UriSpec>("foo:").is_err());
296/// assert!(relative_ref::<UriSpec>("foo:/").is_err());
297/// assert!(relative_ref::<UriSpec>("foo://").is_err());
298/// assert!(relative_ref::<UriSpec>("foo:///").is_err());
299/// assert!(relative_ref::<UriSpec>("foo:////").is_err());
300/// assert!(relative_ref::<UriSpec>("foo://///").is_err());
301/// ```
302///
303/// Some characters and sequences cannot used in an IRI reference.
304///
305/// ```
306/// use iri_string::{spec::UriSpec, validate::relative_ref};
307/// // `<` and `>` cannot directly appear in a relative IRI reference.
308/// assert!(relative_ref::<UriSpec>("<not allowed>").is_err());
309/// // Broken percent encoding cannot appear in a relative IRI reference.
310/// assert!(relative_ref::<UriSpec>("%").is_err());
311/// assert!(relative_ref::<UriSpec>("%GG").is_err());
312/// ```
313///
314/// [relative-ref]: https://www.rfc-editor.org/rfc/rfc3986.html#section-4.2
315/// [`RiRelativeStr`]: ../types/struct.RiRelativeStr.html
316/// [`RiRelativeString`]: ../types/struct.RiRelativeString.html
317pub fn relative_ref<S: Spec>(s: &str) -> Result<(), Error> {
318 parser::validate_relative_ref::<S>(s)
319}
320
321/// Validates [IRI scheme][scheme].
322///
323/// Note that this function does not accept a trailing colon.
324///
325/// Also note that the syntax of the scheme is common between RFC 3986 (URIs)
326/// and RFC 3987 (IRIs).
327///
328/// # Examples
329///
330/// ```
331/// use iri_string::validate::scheme;
332/// assert!(scheme("https").is_ok());
333/// assert!(scheme("file").is_ok());
334/// assert!(scheme("git+ssh").is_ok());
335///
336/// // Colon is syntactically not part of the scheme.
337/// assert!(scheme("colon:").is_err());
338/// // Scheme cannot be empty.
339/// assert!(scheme("").is_err());
340/// // The first character should be alphabetic character.
341/// assert!(scheme("0abc").is_err());
342/// assert!(scheme("+a").is_err());
343/// assert!(scheme("-a").is_err());
344/// ```
345///
346/// [scheme]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3.1
347pub fn scheme(s: &str) -> Result<(), Error> {
348 parser::validate_scheme(s)
349}
350
351/// Validates [IRI authority][authority].
352///
353/// # Examples
354///
355/// ```
356/// use iri_string::{spec::UriSpec, validate::authority};
357/// assert!(authority::<UriSpec>("example.com").is_ok());
358/// assert!(authority::<UriSpec>("subdomain.example.com").is_ok());
359/// assert!(authority::<UriSpec>("no-period").is_ok());
360/// // Though strongly discouraged, this percent-encoded reg-name with
361/// // non-UTF-8 bytes is considered syntactically valid.
362/// assert!(authority::<UriSpec>("non-%99-utf-8").is_ok());
363/// // Empty authority is valid. Remember `file:///` has empty authority.
364/// assert!(authority::<UriSpec>("").is_ok());
365/// assert!(authority::<UriSpec>("127.0.0.1:8080").is_ok());
366/// assert!(authority::<UriSpec>("[::127.0.0.1]:8088").is_ok());
367/// // URI/IRI syntax itself does not have limit on the port number.
368/// assert!(authority::<UriSpec>("[::1]:9999999999").is_ok());
369/// // Syntax for future versions of IP addresses.
370/// assert!(authority::<UriSpec>("[v89ab.1+2,3(4)5&6]").is_ok());
371/// assert!(authority::<UriSpec>("user:password@host").is_ok());
372/// assert!(authority::<UriSpec>("co%3Alon:at%40sign@host:8888").is_ok());
373/// // Percent-encoded non-UTF8 (or even non-ASCII) bytes are valid.
374/// // Users are responsible to validate or reject such unusual input if needed.
375/// assert!(authority::<UriSpec>("not-a-%80-utf8@host").is_ok());
376///
377/// // Invalid percent encodings.
378/// assert!(authority::<UriSpec>("invalid%GGescape@host").is_err());
379/// // Invalid characters.
380/// assert!(authority::<UriSpec>("foo@bar@host").is_err());
381/// assert!(authority::<UriSpec>("slash/is-not-allowed").is_err());
382/// ```
383///
384/// [authority]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3.2
385pub fn authority<S: Spec>(s: &str) -> Result<(), Error> {
386 parser::validate_authority::<S>(s)
387}
388
389/// Validates [IRI host][host].
390///
391/// # Examples
392///
393/// ```
394/// use iri_string::{spec::UriSpec, validate::host};
395/// assert!(host::<UriSpec>("example.com").is_ok());
396/// assert!(host::<UriSpec>("subdomain.example.com").is_ok());
397/// assert!(host::<UriSpec>("no-period").is_ok());
398/// // Though strongly discouraged, this percent-encoded reg-name with
399/// // non-UTF-8 bytes is considered syntactically valid.
400/// assert!(host::<UriSpec>("non-%99-utf-8").is_ok());
401/// // Empty host is valid. Remember `file:///` has empty authority (and empty host).
402/// assert!(host::<UriSpec>("").is_ok());
403/// assert!(host::<UriSpec>("127.0.0.1").is_ok());
404/// assert!(host::<UriSpec>("[::1]").is_ok());
405/// assert!(host::<UriSpec>("[::127.0.0.1]").is_ok());
406/// // Syntax for future versions of IP addresses.
407/// assert!(host::<UriSpec>("[v89ab.1+2,3(4)5&6]").is_ok());
408///
409/// // `port` is not a part of the host.
410/// assert!(host::<UriSpec>("host:8080").is_err());
411/// // `userinfo` is not a part of the host.
412/// assert!(host::<UriSpec>("user:password@host").is_err());
413/// ```
414///
415/// [host]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3.2.2
416pub fn host<S: Spec>(s: &str) -> Result<(), Error> {
417 parser::validate_host::<S>(s)
418}
419
420/// Validates [IRI port][port].
421///
422/// Note that the syntax of the port is common between RFC 3986 (URIs) and
423/// RFC 3987 (IRIs).
424///
425/// Also note that this function does not accept a leading colon.
426///
427/// [host]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3.2.3
428///
429/// # Examples
430///
431/// ```
432/// use iri_string::validate::port;
433/// assert!(port("0").is_ok());
434/// assert!(port("8080").is_ok());
435/// assert!(port("0000080").is_ok());
436/// // URI/IRI syntax itself does not have limit on the port number.
437/// assert!(port("999999999").is_ok());
438///
439/// // The leading colon is not a part of the `port`.
440/// assert!(port(":443").is_err());
441/// ```
442pub fn port(s: &str) -> Result<(), Error> {
443 if s.bytes().all(|b| b.is_ascii_digit()) {
444 Ok(())
445 } else {
446 Err(Error::with_kind(ErrorKind::InvalidPort))
447 }
448}
449
450/// Validates [IRI userinfo][userinfo].
451///
452/// # Examples
453///
454/// ```
455/// use iri_string::{spec::UriSpec, validate::userinfo};
456/// assert!(userinfo::<UriSpec>("user").is_ok());
457/// assert!(userinfo::<UriSpec>("user:password").is_ok());
458/// assert!(userinfo::<UriSpec>("non-%99-utf-8").is_ok());
459/// // Special characters can be included if they are percent-encoded.
460/// assert!(userinfo::<UriSpec>("co%3Alon:at%40sign").is_ok());
461///
462/// // The trailing atsign is not a part of the userinfo.
463/// assert!(userinfo::<UriSpec>("user:password@").is_err());
464/// // Invalid characters.
465/// assert!(userinfo::<UriSpec>("foo@bar").is_err());
466/// assert!(userinfo::<UriSpec>("slash/is-not-allowed").is_err());
467/// ```
468///
469/// [authority]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3.2.1
470pub fn userinfo<S: Spec>(s: &str) -> Result<(), Error> {
471 parser::validate_userinfo::<S>(s)
472}
473
474/// Validates [IRI path][path].
475///
476/// # Examples
477///
478/// ```
479/// use iri_string::{spec::UriSpec, validate::path};
480/// assert!(path::<UriSpec>("").is_ok());
481/// assert!(path::<UriSpec>("foo/bar").is_ok());
482/// assert!(path::<UriSpec>("foo/bar/").is_ok());
483/// assert!(path::<UriSpec>("/foo/bar").is_ok());
484/// assert!(path::<UriSpec>("non-%99-utf-8").is_ok());
485/// // Be careful! This is completely valid (absolute) path, but may be confused
486/// // with an protocol-relative URI, with the authority `foo` and the path `/bar`.
487/// assert!(path::<UriSpec>("//foo/bar").is_ok());
488/// // Be careful! This is completely valid (relative) path, but may be confused
489/// // with an absolute URI, with the scheme `foo` and the path `bar`.
490/// assert!(path::<UriSpec>("foo:bar").is_ok());
491///
492/// // Invalid characters.
493/// assert!(path::<UriSpec>("foo?bar").is_err());
494/// assert!(path::<UriSpec>("foo#bar").is_err());
495/// ```
496///
497/// [path]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3
498pub fn path<S: Spec>(s: &str) -> Result<(), Error> {
499 parser::validate_path::<S>(s)
500}
501
502/// Validates [IRI path segment][segment].
503///
504/// # Examples
505///
506/// ```
507/// use iri_string::{spec::UriSpec, validate::path_segment};
508/// assert!(path_segment::<UriSpec>("").is_ok());
509/// assert!(path_segment::<UriSpec>("escaped-%2F-slash").is_ok());
510/// assert!(path_segment::<UriSpec>("non-%99-utf-8").is_ok());
511///
512/// // A path segment itself cannot contain an unescaped slash.
513/// assert!(path_segment::<UriSpec>("foo/bar").is_err());
514/// ```
515///
516/// [segment]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3
517pub fn path_segment<S: Spec>(s: &str) -> Result<(), Error> {
518 parser::validate_path_segment::<S>(s)
519}
520
521/// Validates [IRI query][query].
522///
523/// This validator corresponds to [`RiQueryStr`] and [`RiQueryString`] types.
524///
525/// Note that the first `?` character in an IRI is not a part of a query.
526/// For example, `https://example.com/?foo#bar` has a query `foo`, **not** `?foo`.
527///
528/// # Examples
529///
530/// This type can have an IRI query.
531/// Note that the IRI `foo://bar/baz?qux#quux` has the query `qux`, **not** `?qux`.
532///
533/// ```
534/// use iri_string::{spec::UriSpec, validate::query};
535/// assert!(query::<UriSpec>("").is_ok());
536/// assert!(query::<UriSpec>("foo").is_ok());
537/// assert!(query::<UriSpec>("foo/bar").is_ok());
538/// assert!(query::<UriSpec>("/foo/bar").is_ok());
539/// assert!(query::<UriSpec>("//foo/bar").is_ok());
540/// assert!(query::<UriSpec>("https://user:pass@example.com:8080").is_ok());
541/// assert!(query::<UriSpec>("https://example.com/").is_ok());
542/// // Question sign `?` can appear in an IRI query.
543/// assert!(query::<UriSpec>("query?again").is_ok());
544/// ```
545///
546/// Some characters and sequences cannot used in a query.
547///
548/// ```
549/// use iri_string::{spec::UriSpec, validate::query};
550/// // `<` and `>` cannot directly appear in an IRI reference.
551/// assert!(query::<UriSpec>("<not allowed>").is_err());
552/// // Broken percent encoding cannot appear in an IRI reference.
553/// assert!(query::<UriSpec>("%").is_err());
554/// assert!(query::<UriSpec>("%GG").is_err());
555/// // Hash sign `#` cannot appear in an IRI query.
556/// assert!(query::<UriSpec>("#hash").is_err());
557/// ```
558///
559/// [query]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3.4
560/// [`RiQueryStr`]: ../types/struct.RiQueryStr.html
561/// [`RiQueryString`]: ../types/struct.RiQueryString.html
562pub fn query<S: Spec>(s: &str) -> Result<(), Error> {
563 parser::validate_query::<S>(s)
564}
565
566/// Validates [IRI fragment][fragment].
567///
568/// This validator corresponds to [`RiFragmentStr`] and [`RiFragmentString`] types.
569///
570/// Note that the first `#` character in an IRI is not a part of a fragment.
571/// For example, `https://example.com/#foo` has a fragment `foo`, **not** `#foo`.
572///
573/// # Examples
574///
575/// This type can have an IRI fragment.
576/// Note that the IRI `foo://bar/baz#qux` has the fragment `qux`, **not** `#qux`.
577///
578/// ```
579/// use iri_string::{spec::UriSpec, validate::fragment};
580/// assert!(fragment::<UriSpec>("").is_ok());
581/// assert!(fragment::<UriSpec>("foo").is_ok());
582/// assert!(fragment::<UriSpec>("foo/bar").is_ok());
583/// assert!(fragment::<UriSpec>("/foo/bar").is_ok());
584/// assert!(fragment::<UriSpec>("//foo/bar").is_ok());
585/// assert!(fragment::<UriSpec>("https://user:pass@example.com:8080").is_ok());
586/// assert!(fragment::<UriSpec>("https://example.com/").is_ok());
587/// ```
588///
589/// Some characters and sequences cannot used in a fragment.
590///
591/// ```
592/// use iri_string::{spec::UriSpec, validate::fragment};
593/// // `<` and `>` cannot directly appear in an IRI reference.
594/// assert!(fragment::<UriSpec>("<not allowed>").is_err());
595/// // Broken percent encoding cannot appear in an IRI reference.
596/// assert!(fragment::<UriSpec>("%").is_err());
597/// assert!(fragment::<UriSpec>("%GG").is_err());
598/// // Hash sign `#` cannot appear in an IRI fragment.
599/// assert!(fragment::<UriSpec>("#hash").is_err());
600/// ```
601///
602/// [fragment]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3.5
603/// [`RiFragmentStr`]: ../types/struct.RiFragmentStr.html
604/// [`RiFragmentString`]: ../types/struct.RiFragmentString.html
605pub fn fragment<S: Spec>(s: &str) -> Result<(), Error> {
606 parser::validate_fragment::<S>(s)
607}