1use std::fmt::{Display, Formatter};
2
3#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
8pub struct Scheme<'a> {
9 scheme: &'a str,
10}
11
12impl<'a> Scheme<'a> {
13 pub unsafe fn new(scheme: &'a str) -> Self {
20 debug_assert!(Self::is_valid(scheme, false));
21
22 Self { scheme }
23 }
24}
25
26impl<'a> Scheme<'a> {
27 fn is_valid_first_char(c: u8, ignore_case: bool) -> bool {
31 c.is_ascii_lowercase() || (ignore_case && c.is_ascii_uppercase())
32 }
33
34 fn is_valid_char(c: u8, ignore_case: bool) -> bool {
36 Self::is_valid_first_char(c, ignore_case)
37 || c.is_ascii_digit()
38 || c == b'+'
39 || c == b'-'
40 || c == b'.'
41 }
42
43 pub fn is_valid(scheme: &'a str, ignore_case: bool) -> bool {
45 !scheme.is_empty()
46 && Self::is_valid_first_char(scheme.as_bytes()[0], ignore_case)
47 && scheme.as_bytes()[1..]
48 .iter()
49 .all(|c| Self::is_valid_char(*c, ignore_case))
50 }
51}
52
53impl<'a> Scheme<'a> {
54 pub const fn as_str(&self) -> &str {
58 self.scheme
59 }
60}
61
62impl<'a> AsRef<str> for Scheme<'a> {
63 fn as_ref(&self) -> &str {
64 self.scheme
65 }
66}
67
68impl<'a> Display for Scheme<'a> {
69 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
70 write!(f, "{}", self.scheme)
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use crate::Scheme;
77
78 #[test]
79 fn new() {
80 let scheme: Scheme = unsafe { Scheme::new("scheme") };
81 assert_eq!(scheme.scheme, "scheme");
82 }
83
84 #[test]
85 fn is_valid() {
86 let test_cases: &[(&str, bool, bool)] = &[
87 ("", false, false),
88 ("A", true, false),
89 ("a", true, true),
90 ("0", false, false),
91 ("a~", false, false),
92 ("az09+-.", true, true),
93 ("azAZ09+-.", true, false),
94 ];
95 for (scheme, expected_ic_true, expected_ic_false) in test_cases {
96 let result: bool = Scheme::is_valid(scheme, true);
97 assert_eq!(result, *expected_ic_true, "scheme={}", scheme);
98
99 let result: bool = Scheme::is_valid(scheme, false);
100 assert_eq!(result, *expected_ic_false, "scheme={}", scheme);
101 }
102 }
103
104 #[test]
105 fn display() {
106 let scheme: Scheme = unsafe { Scheme::new("scheme") };
107 assert_eq!(scheme.as_str(), "scheme");
108 assert_eq!(scheme.as_ref(), "scheme");
109 assert_eq!(scheme.to_string(), "scheme");
110 }
111}