1#![allow(clippy::string_lit_as_bytes)]
2
3use fnv::FnvBuildHasher;
10use lazy_static::lazy_static;
11use std::borrow::Cow;
12use std::collections::HashMap;
13use std::convert::{Infallible, TryFrom};
14use std::error::Error;
15use std::fmt::{self, Display, Formatter};
16use std::hash::{Hash, Hasher};
17use std::str;
18
19use crate::utility::normalize_string;
20
21const MAX_REGISTERED_SCHEME_LENGTH: usize = 36;
24
25const NUMBER_OF_SCHEMES: usize = 304;
27
28#[rustfmt::skip]
30const SCHEME_CHAR_MAP: [u8; 256] = [
31 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b'+', 0, b'-', b'.', 0, b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', 0, 0, 0, 0, 0, 0, 0, b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', 0, 0, 0, 0, 0, 0, b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
49
50macro_rules! schemes {
51 (
52 $(
53 ($variant:ident, $name:expr, $status:expr);
54 )+
55 ) => {
56 lazy_static! {
57 static ref SCHEME_NAME_MAP: HashMap<&'static [u8], Scheme<'static>, FnvBuildHasher> = {
60 let mut map = HashMap::with_capacity_and_hasher(
61 NUMBER_OF_SCHEMES,
62 FnvBuildHasher::default()
63 );
64
65 $(
66 assert!(map.insert($name.as_bytes(), Scheme::$variant).is_none());
67 )+
68
69 map
70 };
71 }
72
73 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
83 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84 #[non_exhaustive]
85 pub enum Scheme<'scheme> {
86 $(
87 $variant,
88 )+
89 Unregistered(UnregisteredScheme<'scheme>)
90 }
91
92 impl<'scheme> Scheme<'scheme> {
93 pub fn as_borrowed(&self) -> Scheme {
95 use self::Scheme::*;
96
97 match self {
98 $(
99 $variant => $variant,
100 )+
101 Unregistered(scheme) => Unregistered(scheme.as_borrowed())
102 }
103 }
104
105 pub fn as_str(&self) -> &str {
124 use self::Scheme::*;
125
126 match self {
127 $(
128 $variant => $name,
129 )+
130 Unregistered(scheme) => scheme.as_str()
131 }
132 }
133
134 pub fn into_owned(self) -> Scheme<'static> {
144 use self::Scheme::*;
145
146 match self {
147 $(
148 $variant => $variant,
149 )+
150 Unregistered(scheme) => Unregistered(scheme.into_owned())
151 }
152 }
153
154 pub fn status(&self) -> SchemeStatus {
164 use self::Scheme::*;
165
166 match self {
167 $(
168 $variant => $status,
169 )+
170 Unregistered(_) => SchemeStatus::Unregistered
171 }
172 }
173 }
174
175 pub(crate) fn parse_scheme(value: &[u8]) -> Result<(Scheme, &[u8]), SchemeError> {
177 fn unregistered_scheme(value: &[u8], normalized: bool) -> Scheme {
178 let scheme = unsafe { str::from_utf8_unchecked(value) };
180 Scheme::Unregistered(UnregisteredScheme{
181 normalized,
182 scheme:Cow::from(scheme)
183 })
184 }
185
186 if !value.iter().next().ok_or(SchemeError::Empty)?.is_ascii_alphabetic() {
187 return Err(SchemeError::StartsWithNonAlphabetic);
188 }
189
190 let mut end_index = 0;
191 let mut lowercase_scheme = [0; MAX_REGISTERED_SCHEME_LENGTH];
192 let mut normalized = true;
193
194 for &byte in value.iter() {
195 match SCHEME_CHAR_MAP[byte as usize] {
196 0 if byte == b':' => break,
197 0 => return Err(SchemeError::InvalidCharacter),
198 _ => {
199 if byte >= b'A' && byte <= b'Z' {
200 normalized = false;
201 }
202
203 if end_index + 1 < MAX_REGISTERED_SCHEME_LENGTH {
204 lowercase_scheme[end_index] = byte.to_ascii_lowercase();
205 }
206
207 end_index += 1;
208 }
209 }
210 }
211
212 let (value, rest) = value.split_at(end_index);
213
214 if end_index > MAX_REGISTERED_SCHEME_LENGTH {
218 return Ok((unregistered_scheme(value, normalized), rest));
219 }
220
221 let scheme = SCHEME_NAME_MAP
222 .get(&lowercase_scheme[..end_index])
223 .cloned()
224 .unwrap_or_else(|| unregistered_scheme(value, normalized));
225
226 Ok((scheme, rest))
227 }
228 }
229}
230
231impl Scheme<'_> {
232 pub fn is_normalized(&self) -> bool {
258 match self {
259 Scheme::Unregistered(scheme) => scheme.is_normalized(),
260 _ => true,
261 }
262 }
263
264 pub fn normalize(&mut self) {
282 if let Scheme::Unregistered(scheme) = self {
283 scheme.normalize();
284 }
285 }
286}
287
288impl AsRef<[u8]> for Scheme<'_> {
289 fn as_ref(&self) -> &[u8] {
290 self.as_str().as_bytes()
291 }
292}
293
294impl AsRef<str> for Scheme<'_> {
295 fn as_ref(&self) -> &str {
296 self.as_str()
297 }
298}
299
300impl Display for Scheme<'_> {
301 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
302 formatter.write_str(self.as_str())
303 }
304}
305
306impl<'scheme> From<Scheme<'scheme>> for String {
307 fn from(value: Scheme<'scheme>) -> Self {
308 value.to_string()
309 }
310}
311
312impl PartialEq<[u8]> for Scheme<'_> {
313 fn eq(&self, other: &[u8]) -> bool {
314 self.as_str().as_bytes().eq_ignore_ascii_case(other)
315 }
316}
317
318impl<'scheme> PartialEq<Scheme<'scheme>> for [u8] {
319 fn eq(&self, other: &Scheme<'scheme>) -> bool {
320 other == self
321 }
322}
323
324impl<'a> PartialEq<&'a [u8]> for Scheme<'_> {
325 fn eq(&self, other: &&'a [u8]) -> bool {
326 self == *other
327 }
328}
329
330impl<'a, 'scheme> PartialEq<Scheme<'scheme>> for &'a [u8] {
331 fn eq(&self, other: &Scheme<'scheme>) -> bool {
332 other == *self
333 }
334}
335
336impl PartialEq<str> for Scheme<'_> {
337 fn eq(&self, other: &str) -> bool {
338 self == other.as_bytes()
339 }
340}
341
342impl<'scheme> PartialEq<Scheme<'scheme>> for str {
343 fn eq(&self, other: &Scheme<'scheme>) -> bool {
344 other == self.as_bytes()
345 }
346}
347
348impl<'a> PartialEq<&'a str> for Scheme<'_> {
349 fn eq(&self, other: &&'a str) -> bool {
350 self == other.as_bytes()
351 }
352}
353
354impl<'a, 'scheme> PartialEq<Scheme<'scheme>> for &'a str {
355 fn eq(&self, other: &Scheme<'scheme>) -> bool {
356 other == self.as_bytes()
357 }
358}
359
360impl<'scheme> TryFrom<&'scheme [u8]> for Scheme<'scheme> {
361 type Error = SchemeError;
362
363 fn try_from(value: &'scheme [u8]) -> Result<Scheme<'scheme>, Self::Error> {
364 let (scheme, rest) = parse_scheme(value)?;
365
366 if rest.is_empty() {
367 Ok(scheme)
368 } else {
369 Err(SchemeError::InvalidCharacter)
370 }
371 }
372}
373
374impl<'scheme> TryFrom<&'scheme str> for Scheme<'scheme> {
375 type Error = SchemeError;
376
377 fn try_from(value: &'scheme str) -> Result<Scheme<'scheme>, Self::Error> {
378 Scheme::try_from(value.as_bytes())
379 }
380}
381
382#[derive(Clone, Debug)]
387#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
388pub struct UnregisteredScheme<'scheme> {
389 normalized: bool,
391
392 scheme: Cow<'scheme, str>,
394}
395
396impl UnregisteredScheme<'_> {
397 pub fn as_borrowed(&self) -> UnregisteredScheme {
400 use self::Cow::*;
401
402 let scheme = match &self.scheme {
403 Borrowed(borrowed) => *borrowed,
404 Owned(owned) => owned.as_str(),
405 };
406
407 UnregisteredScheme {
408 normalized: self.normalized,
409 scheme: Cow::Borrowed(scheme),
410 }
411 }
412
413 pub fn as_str(&self) -> &str {
428 &self.scheme
429 }
430
431 pub fn into_owned(self) -> UnregisteredScheme<'static> {
440 UnregisteredScheme {
441 normalized: self.normalized,
442 scheme: Cow::from(self.scheme.into_owned()),
443 }
444 }
445
446 pub fn is_normalized(&self) -> bool {
468 self.normalized
469 }
470
471 pub fn normalize(&mut self) {
489 if !self.normalized {
490 unsafe { normalize_string(&mut self.scheme.to_mut(), true) };
492 self.normalized = true;
493 }
494 }
495}
496
497impl AsRef<[u8]> for UnregisteredScheme<'_> {
498 fn as_ref(&self) -> &[u8] {
499 self.scheme.as_bytes()
500 }
501}
502
503impl AsRef<str> for UnregisteredScheme<'_> {
504 fn as_ref(&self) -> &str {
505 &self.scheme
506 }
507}
508
509impl Display for UnregisteredScheme<'_> {
510 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
511 formatter.write_str(&self.scheme)
512 }
513}
514
515impl Eq for UnregisteredScheme<'_> {}
516
517impl<'scheme> From<UnregisteredScheme<'scheme>> for String {
518 fn from(value: UnregisteredScheme<'scheme>) -> Self {
519 value.to_string()
520 }
521}
522
523impl Hash for UnregisteredScheme<'_> {
524 fn hash<H>(&self, state: &mut H)
525 where
526 H: Hasher,
527 {
528 self.scheme.to_lowercase().hash(state)
529 }
530}
531
532impl PartialEq for UnregisteredScheme<'_> {
533 fn eq(&self, other: &UnregisteredScheme) -> bool {
534 *self == *other.scheme.as_bytes()
535 }
536}
537
538impl PartialEq<[u8]> for UnregisteredScheme<'_> {
539 fn eq(&self, other: &[u8]) -> bool {
540 self.scheme.as_bytes().eq_ignore_ascii_case(&other)
541 }
542}
543
544impl<'scheme> PartialEq<UnregisteredScheme<'scheme>> for [u8] {
545 fn eq(&self, other: &UnregisteredScheme<'scheme>) -> bool {
546 other == self
547 }
548}
549
550impl<'a> PartialEq<&'a [u8]> for UnregisteredScheme<'_> {
551 fn eq(&self, other: &&'a [u8]) -> bool {
552 self == *other
553 }
554}
555
556impl<'a, 'scheme> PartialEq<UnregisteredScheme<'scheme>> for &'a [u8] {
557 fn eq(&self, other: &UnregisteredScheme<'scheme>) -> bool {
558 other == *self
559 }
560}
561
562impl PartialEq<str> for UnregisteredScheme<'_> {
563 fn eq(&self, other: &str) -> bool {
564 self == other.as_bytes()
565 }
566}
567
568impl<'scheme> PartialEq<UnregisteredScheme<'scheme>> for str {
569 fn eq(&self, other: &UnregisteredScheme<'scheme>) -> bool {
570 other == self.as_bytes()
571 }
572}
573
574impl<'a> PartialEq<&'a str> for UnregisteredScheme<'_> {
575 fn eq(&self, other: &&'a str) -> bool {
576 self == other.as_bytes()
577 }
578}
579
580impl<'a, 'scheme> PartialEq<UnregisteredScheme<'scheme>> for &'a str {
581 fn eq(&self, other: &UnregisteredScheme<'scheme>) -> bool {
582 other == self.as_bytes()
583 }
584}
585
586impl<'scheme> TryFrom<&'scheme [u8]> for UnregisteredScheme<'scheme> {
587 type Error = UnregisteredSchemeError;
588
589 fn try_from(value: &'scheme [u8]) -> Result<Self, Self::Error> {
590 match Scheme::try_from(value) {
591 Ok(Scheme::Unregistered(scheme)) => Ok(scheme),
592 _ => Err(UnregisteredSchemeError),
593 }
594 }
595}
596
597impl<'scheme> TryFrom<&'scheme str> for UnregisteredScheme<'scheme> {
598 type Error = UnregisteredSchemeError;
599
600 fn try_from(value: &'scheme str) -> Result<Self, Self::Error> {
601 UnregisteredScheme::try_from(value.as_bytes())
602 }
603}
604
605#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
607#[non_exhaustive]
608pub enum SchemeError {
609 Empty,
611
612 InvalidCharacter,
614
615 StartsWithNonAlphabetic,
617}
618
619impl Display for SchemeError {
620 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
621 use self::SchemeError::*;
622
623 match self {
624 Empty => write!(formatter, "scheme is empty"),
625 InvalidCharacter => write!(formatter, "invalid scheme character"),
626 StartsWithNonAlphabetic => {
627 write!(formatter, "scheme starts with non-alphabetic character")
628 }
629 }
630 }
631}
632
633impl Error for SchemeError {}
634
635impl From<Infallible> for SchemeError {
636 fn from(_: Infallible) -> Self {
637 SchemeError::InvalidCharacter
638 }
639}
640
641#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
644pub struct UnregisteredSchemeError;
645
646impl Display for UnregisteredSchemeError {
647 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
648 write!(formatter, "invalid unregistered scheme")
649 }
650}
651
652impl Error for UnregisteredSchemeError {}
653
654#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
657pub enum SchemeStatus {
658 Historical,
661
662 Permanent,
664
665 Provisional,
667
668 Unregistered,
671}
672
673impl SchemeStatus {
674 pub fn is_historical(self) -> bool {
685 match self {
686 SchemeStatus::Historical => true,
687 _ => false,
688 }
689 }
690
691 pub fn is_permanent(self) -> bool {
702 match self {
703 SchemeStatus::Permanent => true,
704 _ => false,
705 }
706 }
707
708 pub fn is_provisional(self) -> bool {
719 match self {
720 SchemeStatus::Provisional => true,
721 _ => false,
722 }
723 }
724
725 pub fn is_unregistered(self) -> bool {
739 match self {
740 SchemeStatus::Unregistered => true,
741 _ => false,
742 }
743 }
744}
745
746schemes! {
747 (AAA, "aaa", SchemeStatus::Permanent);
748 (AAAS, "aaas", SchemeStatus::Permanent);
749 (About, "about", SchemeStatus::Permanent);
750 (ACAP, "acap", SchemeStatus::Permanent);
751 (ACCT, "acat", SchemeStatus::Permanent);
752 (ACR, "acr", SchemeStatus::Provisional);
753 (AdiumXtra, "adiumxtra", SchemeStatus::Provisional);
754 (AFP, "afp", SchemeStatus::Provisional);
755 (AFS, "afs", SchemeStatus::Provisional);
756 (AIM, "aim", SchemeStatus::Provisional);
757 (AMSS, "amss", SchemeStatus::Provisional);
758 (Android, "android", SchemeStatus::Provisional);
759 (AppData, "appdata", SchemeStatus::Provisional);
760 (APT, "apt", SchemeStatus::Provisional);
761 (Attachment, "attachment", SchemeStatus::Provisional);
762 (AW, "aw", SchemeStatus::Provisional);
763 (Barion, "barion", SchemeStatus::Provisional);
764 (BeShare, "beshare", SchemeStatus::Provisional);
765 (Bitcoin, "bitcoin", SchemeStatus::Provisional);
766 (BitcoinCash, "bitcoincash", SchemeStatus::Provisional);
767 (Blob, "blob", SchemeStatus::Provisional);
768 (Bolo, "bolo", SchemeStatus::Provisional);
769 (BrowserExt, "browserext", SchemeStatus::Provisional);
770 (Calculator, "calculator", SchemeStatus::Provisional);
771 (CallTo, "callto", SchemeStatus::Provisional);
772 (CAP, "cap", SchemeStatus::Permanent);
773 (Cast, "cast", SchemeStatus::Provisional);
774 (Casts, "casts", SchemeStatus::Provisional);
775 (Chrome, "chrome", SchemeStatus::Provisional);
776 (ChromeExtension, "chrome-extension", SchemeStatus::Provisional);
777 (CID, "cid", SchemeStatus::Permanent);
778 (CoAP, "coap", SchemeStatus::Permanent);
779 (CoAPTCP, "coap+tcp", SchemeStatus::Permanent);
780 (CoAPWS, "coap+ws", SchemeStatus::Permanent);
781 (CoAPS, "coaps", SchemeStatus::Permanent);
782 (CoAPSTCP, "coaps+tcp", SchemeStatus::Permanent);
783 (CoAPSWS, "coaps+ws", SchemeStatus::Permanent);
784 (ComEventBriteAttendee, "com-eventbrite-attendee", SchemeStatus::Provisional);
785 (Content, "content", SchemeStatus::Provisional);
786 (Conti, "conti", SchemeStatus::Provisional);
787 (CRID, "crid", SchemeStatus::Permanent);
788 (CVS, "cvs", SchemeStatus::Provisional);
789 (DAB, "dab", SchemeStatus::Provisional);
790 (Data, "data", SchemeStatus::Permanent);
791 (DAV, "dav", SchemeStatus::Permanent);
792 (Diaspora, "diaspora", SchemeStatus::Provisional);
793 (DICT, "dict", SchemeStatus::Permanent);
794 (DID, "did", SchemeStatus::Provisional);
795 (DIS, "dis", SchemeStatus::Provisional);
796 (DLNAPlayContainer, "dlna-playcontainer", SchemeStatus::Provisional);
797 (DLNAPlaySingle, "dlna-playsingle", SchemeStatus::Provisional);
798 (DNS, "dns", SchemeStatus::Permanent);
799 (DNTP, "dntp", SchemeStatus::Provisional);
800 (DPP, "dpp", SchemeStatus::Provisional);
801 (DRM, "drm", SchemeStatus::Provisional);
802 (Drop, "drop", SchemeStatus::Provisional);
803 (DTN, "dtn", SchemeStatus::Provisional);
804 (DVB, "dvb", SchemeStatus::Provisional);
805 (ED2K, "ed2k", SchemeStatus::Provisional);
806 (ELSI, "elsi", SchemeStatus::Provisional);
807 (Example, "example", SchemeStatus::Permanent);
808 (FaceTime, "facetime", SchemeStatus::Provisional);
809 (Fax, "fax", SchemeStatus::Historical);
810 (Feed, "feed", SchemeStatus::Provisional);
811 (FeedReady, "feedready", SchemeStatus::Provisional);
812 (File, "file", SchemeStatus::Permanent);
813 (FileSystem, "filesystem", SchemeStatus::Historical);
814 (Finger, "finger", SchemeStatus::Provisional);
815 (Fish, "fish", SchemeStatus::Provisional);
816 (FM, "fm", SchemeStatus::Provisional);
817 (FTP, "ftp", SchemeStatus::Permanent);
818 (FuchsiaPkg, "fuchsia-pkg", SchemeStatus::Provisional);
819 (Geo, "geo", SchemeStatus::Permanent);
820 (GG, "gg", SchemeStatus::Provisional);
821 (Git, "git", SchemeStatus::Provisional);
822 (GizmoProject, "gizmoproject", SchemeStatus::Provisional);
823 (Go, "go", SchemeStatus::Permanent);
824 (Gopher, "gopher", SchemeStatus::Permanent);
825 (Graph, "graph", SchemeStatus::Provisional);
826 (GTalk, "gtalk", SchemeStatus::Provisional);
827 (H323, "h323", SchemeStatus::Permanent);
828 (HAM, "ham", SchemeStatus::Provisional);
829 (HCAP, "hcap", SchemeStatus::Provisional);
830 (HCP, "hcp", SchemeStatus::Provisional);
831 (HTTP, "http", SchemeStatus::Permanent);
832 (HTTPS, "https", SchemeStatus::Permanent);
833 (HXXP, "hxxp", SchemeStatus::Provisional);
834 (HXXPS, "hxxps", SchemeStatus::Provisional);
835 (HydraZone, "hydrazone", SchemeStatus::Provisional);
836 (IAX, "iax", SchemeStatus::Permanent);
837 (ICAP, "icap", SchemeStatus::Permanent);
838 (Icon, "icon", SchemeStatus::Provisional);
839 (IM, "im", SchemeStatus::Permanent);
840 (IMAP, "imap", SchemeStatus::Permanent);
841 (Info, "info", SchemeStatus::Permanent);
842 (IoTDisc, "iotdisc", SchemeStatus::Provisional);
843 (IPN, "ipn", SchemeStatus::Provisional);
844 (IPP, "ipp", SchemeStatus::Permanent);
845 (IPPS, "ipps", SchemeStatus::Permanent);
846 (IRC, "irc", SchemeStatus::Provisional);
847 (IRC6, "irc6", SchemeStatus::Provisional);
848 (IRCS, "ircs", SchemeStatus::Provisional);
849 (IRIS, "iris", SchemeStatus::Permanent);
850 (IRISBEEP, "iris.beep", SchemeStatus::Permanent);
851 (IRISLWZ, "iris.lwz", SchemeStatus::Permanent);
852 (IRISXPC, "iris.xpc", SchemeStatus::Permanent);
853 (IRISXPCS, "iris.xpcs", SchemeStatus::Permanent);
854 (IsoStore, "isostore", SchemeStatus::Provisional);
855 (ITMS, "itms", SchemeStatus::Provisional);
856 (Jabber, "jabber", SchemeStatus::Permanent);
857 (JAR, "jar", SchemeStatus::Provisional);
858 (JMS, "jms", SchemeStatus::Provisional);
859 (KeyParc, "keyparc", SchemeStatus::Provisional);
860 (LastFM, "lastfm", SchemeStatus::Provisional);
861 (LDAP, "ldap", SchemeStatus::Permanent);
862 (LDAPS, "ldaps", SchemeStatus::Provisional);
863 (LoRaWAN, "lorawan", SchemeStatus::Provisional);
864 (LVLT, "lvlt", SchemeStatus::Provisional);
865 (Magnet, "magnet", SchemeStatus::Provisional);
866 (MailServer, "mailserver", SchemeStatus::Historical);
867 (MailTo, "mailto", SchemeStatus::Permanent);
868 (Maps, "maps", SchemeStatus::Provisional);
869 (Market, "market", SchemeStatus::Provisional);
870 (Message, "message", SchemeStatus::Provisional);
871 (MicrosoftWindowsCamera, "microsoft.windows.camera", SchemeStatus::Provisional);
872 (MicrosoftWindowsCameraMultiPicker, "microsoft.windows.camera.multipicker", SchemeStatus::Provisional);
873 (MicrosoftWindowsCameraPicker, "microsoft.windows.camera.picker", SchemeStatus::Provisional);
874 (MID, "mid", SchemeStatus::Permanent);
875 (MMS, "mms", SchemeStatus::Provisional);
876 (Modem, "modem", SchemeStatus::Historical);
877 (MongoDB, "mongodb", SchemeStatus::Provisional);
878 (Moz, "moz", SchemeStatus::Provisional);
879 (MSAccess, "ms-access", SchemeStatus::Provisional);
880 (MSBrowserExtension, "ms-browser-extension", SchemeStatus::Provisional);
881 (MSCalculator, "ms-calculator", SchemeStatus::Provisional);
882 (MSDriveTo, "ms-drive-to", SchemeStatus::Provisional);
883 (MSEnrollment, "ms-enrollment", SchemeStatus::Provisional);
884 (MSExcel, "ms-excel", SchemeStatus::Provisional);
885 (MSEyeControlSpeech, "ms-eyecontrolspeech", SchemeStatus::Provisional);
886 (MSGameBarServices, "ms-gamebaresrvices", SchemeStatus::Provisional);
887 (MSGamingOverlay, "ms-gamingoverlay", SchemeStatus::Provisional);
888 (MSGetOffice, "ms-getoffice", SchemeStatus::Provisional);
889 (MSHelp, "ms-help", SchemeStatus::Provisional);
890 (MSInfoPath, "ms-infopath", SchemeStatus::Provisional);
891 (MSInputApp, "ms-inputapp", SchemeStatus::Provisional);
892 (MSLockScreenComponentConfig, "ms-lockscreencomponent-config", SchemeStatus::Provisional);
893 (MSMediaStreamID, "ms-media-stream-id", SchemeStatus::Provisional);
894 (MSMixedRealityCapture, "ms-mixedrealitycapture", SchemeStatus::Provisional);
895 (MSOfficeApp, "ms-officeapp", SchemeStatus::Provisional);
896 (MSPeople, "ms-people", SchemeStatus::Provisional);
897 (MSProject, "ms-project", SchemeStatus::Provisional);
898 (MSPowerPoint, "ms-powerpoint", SchemeStatus::Provisional);
899 (MSPublisher, "ms-publisher", SchemeStatus::Provisional);
900 (MSRestoreTabCompanion, "ms-restoretabcompanion", SchemeStatus::Provisional);
901 (MSS, "mss", SchemeStatus::Provisional);
902 (MSScreenClip, "ms-screenclip", SchemeStatus::Provisional);
903 (MSScreenSketch, "ms-screensketch", SchemeStatus::Provisional);
904 (MSSearch, "ms-search", SchemeStatus::Provisional);
905 (MSSearchRepair, "ms-search-repair", SchemeStatus::Provisional);
906 (MSSecondaryScreenController, "ms-secondary-screen-controller", SchemeStatus::Provisional);
907 (MSSeocndaryScreenSetup, "ms-secondary-screen-setup", SchemeStatus::Provisional);
908 (MSSettings, "ms-settings", SchemeStatus::Provisional);
909 (MSSettingsAirplaneMode, "ms-settings-airplanemode", SchemeStatus::Provisional);
910 (MSSettingsBluetooth, "ms-settings-bluetooth", SchemeStatus::Provisional);
911 (MSSettingsCamera, "ms-settings-camera", SchemeStatus::Provisional);
912 (MSSettingsCellular, "ms-settings-cellular", SchemeStatus::Provisional);
913 (MSSettingsCloudStorage, "ms-settings-cloudstorage", SchemeStatus::Provisional);
914 (MSSettingsConnectableDevices, "ms-settings-connectabledevices", SchemeStatus::Provisional);
915 (MSSettingsDisplaysTopology, "ms-settings-displays-topology", SchemeStatus::Provisional);
916 (MSSettingsEmailAndAccounts, "ms-settings-emailandaccounts", SchemeStatus::Provisional);
917 (MSSettingsLanguage, "ms-settings-language", SchemeStatus::Provisional);
918 (MSSettingsLocation, "ms-settings-location", SchemeStatus::Provisional);
919 (MSSettingsLock, "ms-settings-lock", SchemeStatus::Provisional);
920 (MSSettingsNFCTransactions, "ms-settings-nfctransactions", SchemeStatus::Provisional);
921 (MSSettingsNotifications, "ms-settings-notifications", SchemeStatus::Provisional);
922 (MSSettingsPower, "ms-settings-power", SchemeStatus::Provisional);
923 (MSSettingsPrivacy, "ms-settings-privacy", SchemeStatus::Provisional);
924 (MSSettingsProximity, "ms-settings-proximity", SchemeStatus::Provisional);
925 (MSSettingsScreenRotation, "ms-settings-screenrotation", SchemeStatus::Provisional);
926 (MSSettingsWiFi, "ms-settings-wifi", SchemeStatus::Provisional);
927 (MSSettingsWorkplace, "ms-settings-workplace", SchemeStatus::Provisional);
928 (MSSPD, "ms-spd", SchemeStatus::Provisional);
929 (MSSTTOverlay, "ms-sttoverlay", SchemeStatus::Provisional);
930 (MSTransitTo, "ms-transit-to", SchemeStatus::Provisional);
931 (MSUserActivitySet, "ms-useractivityset", SchemeStatus::Provisional);
932 (MSVirtualTouchPad, "ms-virtualtouchpad", SchemeStatus::Provisional);
933 (MSVisio, "ms-visio", SchemeStatus::Provisional);
934 (MSWalkTo, "ms-walk-to", SchemeStatus::Provisional);
935 (MSWhiteboard, "ms-whiteboard", SchemeStatus::Provisional);
936 (MSWhiteboardCMD, "ms-whiteboard-cmd", SchemeStatus::Provisional);
937 (MSWord, "ms-word", SchemeStatus::Provisional);
938 (MSNIM, "msnim", SchemeStatus::Provisional);
939 (MSRP, "msrp", SchemeStatus::Permanent);
940 (MSRPS, "msrps", SchemeStatus::Permanent);
941 (MTQP, "mtqp", SchemeStatus::Permanent);
942 (Mumble, "mumble", SchemeStatus::Provisional);
943 (MUpdate, "mupdate", SchemeStatus::Permanent);
944 (MVN, "mvn", SchemeStatus::Provisional);
945 (News, "news", SchemeStatus::Permanent);
946 (NFS, "nfs", SchemeStatus::Permanent);
947 (NI, "ni", SchemeStatus::Permanent);
948 (NIH, "nih", SchemeStatus::Permanent);
949 (NNTP, "nntp", SchemeStatus::Permanent);
950 (Notes, "notes", SchemeStatus::Provisional);
951 (OCF, "ocf", SchemeStatus::Provisional);
952 (OID, "oid", SchemeStatus::Provisional);
953 (OneNote, "onenote", SchemeStatus::Provisional);
954 (OneNoteCMD, "onenote-cmd", SchemeStatus::Provisional);
955 (OpaqueLockToken, "opaquelocktoken", SchemeStatus::Permanent);
956 (OpenPGP4FPR, "openpgp4fpr", SchemeStatus::Provisional);
957 (Pack, "pack", SchemeStatus::Historical);
958 (Palm, "palm", SchemeStatus::Provisional);
959 (Paparazzi, "paparazzi", SchemeStatus::Provisional);
960 (PKCS11, "pkcs11", SchemeStatus::Permanent);
961 (Platform, "platform", SchemeStatus::Provisional);
962 (POP, "pop", SchemeStatus::Permanent);
963 (Pres, "pres", SchemeStatus::Permanent);
964 (Prospero, "prospero", SchemeStatus::Historical);
965 (Proxy, "proxy", SchemeStatus::Provisional);
966 (PWID, "pwid", SchemeStatus::Provisional);
967 (PSYC, "psyc", SchemeStatus::Provisional);
968 (QB, "qb", SchemeStatus::Provisional);
969 (Query, "query", SchemeStatus::Provisional);
970 (Redis, "redis", SchemeStatus::Provisional);
971 (RedisS, "rediss", SchemeStatus::Provisional);
972 (Reload, "reload", SchemeStatus::Permanent);
973 (Res, "res", SchemeStatus::Provisional);
974 (Resource, "resource", SchemeStatus::Provisional);
975 (RMI, "rmi", SchemeStatus::Provisional);
976 (RSync, "rsync", SchemeStatus::Provisional);
977 (RTMFP, "rtmfp", SchemeStatus::Provisional);
978 (RTMP, "rtmp", SchemeStatus::Provisional);
979 (RTSP, "rtsp", SchemeStatus::Permanent);
980 (RTSPS, "rtsps", SchemeStatus::Permanent);
981 (RTSPU, "rtspu", SchemeStatus::Permanent);
982 (SecondLife, "secondlife", SchemeStatus::Provisional);
983 (Service, "service", SchemeStatus::Permanent);
984 (Session, "session", SchemeStatus::Permanent);
985 (SFTP, "sftp", SchemeStatus::Provisional);
986 (SGN, "sgn", SchemeStatus::Provisional);
987 (SHTTP, "shttp", SchemeStatus::Permanent);
988 (Sieve, "sieve", SchemeStatus::Permanent);
989 (SIP, "sip", SchemeStatus::Permanent);
990 (SIPS, "sips", SchemeStatus::Permanent);
991 (SimpleLedger, "simpleledger", SchemeStatus::Provisional);
992 (Skype, "skype", SchemeStatus::Provisional);
993 (SMB, "smb", SchemeStatus::Provisional);
994 (SMS, "sms", SchemeStatus::Permanent);
995 (SMTP, "smtp", SchemeStatus::Provisional);
996 (SNews, "snews", SchemeStatus::Historical);
997 (SNMP, "snmp", SchemeStatus::Permanent);
998 (SOAPBEEP, "soap.beep", SchemeStatus::Permanent);
999 (SOAPBEEPS, "soap.beeps", SchemeStatus::Permanent);
1000 (Soldat, "soldat", SchemeStatus::Provisional);
1001 (SPIFFE, "spiffe", SchemeStatus::Provisional);
1002 (Spotify, "spotify", SchemeStatus::Provisional);
1003 (SSH, "ssh", SchemeStatus::Provisional);
1004 (Steam, "steam", SchemeStatus::Provisional);
1005 (STUN, "stun", SchemeStatus::Permanent);
1006 (STUNS, "stuns", SchemeStatus::Permanent);
1007 (Submit, "submit", SchemeStatus::Provisional);
1008 (SVN, "svn", SchemeStatus::Provisional);
1009 (Tag, "tag", SchemeStatus::Permanent);
1010 (TeamSpeak, "teamspeak", SchemeStatus::Provisional);
1011 (Tel, "tel", SchemeStatus::Permanent);
1012 (TeliaEID, "teliaeid", SchemeStatus::Provisional);
1013 (Telnet, "telnet", SchemeStatus::Permanent);
1014 (TFTP, "tftp", SchemeStatus::Permanent);
1015 (Things, "things", SchemeStatus::Provisional);
1016 (ThisMessage, "thismessage", SchemeStatus::Permanent);
1017 (TIP, "tip", SchemeStatus::Permanent);
1018 (TN3270, "tn3270", SchemeStatus::Permanent);
1019 (Tool, "tool", SchemeStatus::Provisional);
1020 (TURN, "turn", SchemeStatus::Permanent);
1021 (TURNS, "turns", SchemeStatus::Permanent);
1022 (TV, "tv", SchemeStatus::Permanent);
1023 (UDP, "udp", SchemeStatus::Provisional);
1024 (Unreal, "unreal", SchemeStatus::Provisional);
1025 (URN, "urn", SchemeStatus::Permanent);
1026 (UT2004, "ut2004", SchemeStatus::Provisional);
1027 (VEvent, "v-event", SchemeStatus::Provisional);
1028 (VEMMI, "vemmi", SchemeStatus::Permanent);
1029 (Ventrilo, "ventrilo", SchemeStatus::Provisional);
1030 (Videotex, "videotex", SchemeStatus::Historical);
1031 (VNC, "vnc", SchemeStatus::Permanent);
1032 (ViewSource, "view-source", SchemeStatus::Provisional);
1033 (WAIS, "wais", SchemeStatus::Historical);
1034 (Webcal, "webcal", SchemeStatus::Provisional);
1035 (WPID, "wpid", SchemeStatus::Historical);
1036 (WS, "ws", SchemeStatus::Permanent);
1037 (WSS, "wss", SchemeStatus::Permanent);
1038 (WTAI, "wtai", SchemeStatus::Provisional);
1039 (WYCIWYG, "wyciwyg", SchemeStatus::Provisional);
1040 (XCON, "xcon", SchemeStatus::Permanent);
1041 (XCONUserID, "xcon-userid", SchemeStatus::Permanent);
1042 (Xfire, "xfire", SchemeStatus::Provisional);
1043 (XMLRPCBEEP, "xmlrpc.beep", SchemeStatus::Permanent);
1044 (XMLRPCBEEPS, "xmlrpc.beeps", SchemeStatus::Permanent);
1045 (XMPP, "xmpp", SchemeStatus::Permanent);
1046 (XRI, "xri", SchemeStatus::Provisional);
1047 (YMSGR, "ymsgr", SchemeStatus::Provisional);
1048 (Z3950, "z39.50", SchemeStatus::Historical);
1049 (Z3950R, "z39.50r", SchemeStatus::Permanent);
1050 (Z3950S, "z39.50s", SchemeStatus::Permanent);
1051}
1052
1053#[cfg(test)]
1054mod test {
1055 use super::*;
1056
1057 #[test]
1058 fn test_scheme_normalize() {
1059 fn test_case(value: &str, expected: &str) {
1060 let mut scheme = Scheme::try_from(value).unwrap();
1061 scheme.normalize();
1062 assert_eq!(scheme, expected);
1063 }
1064
1065 test_case("http", "http");
1066 test_case("SCHEME", "scheme");
1067 }
1068
1069 #[test]
1070 fn test_scheme_parse() {
1071 use self::SchemeError::*;
1072
1073 assert_eq!(Scheme::try_from("scheme").unwrap(), "scheme");
1074 assert_eq!(Scheme::try_from("HTTP").unwrap(), "http");
1075 assert_eq!(Scheme::try_from("SCHEME").unwrap(), "SCHEME");
1076
1077 assert_eq!(Scheme::try_from(""), Err(Empty));
1078 assert_eq!(Scheme::try_from("a:"), Err(InvalidCharacter));
1079 assert_eq!(Scheme::try_from("1"), Err(StartsWithNonAlphabetic));
1080 }
1081}