1#[derive(Copy, Clone, PartialEq, Eq)]
2pub enum Segment {
3 Base,
5 Nz,
7 NzNc,
9}
10
11pub fn is_segment(value: &str, segment: Segment) -> bool {
13 if (segment == Segment::Nz || segment == Segment::NzNc) && value.is_empty() {
14 return false;
15 }
16
17 let mut processing_pct_encoded = false;
18 let mut pct_encoded_char = 0usize;
19
20 for c in value.chars() {
21 if processing_pct_encoded {
23 if pct_encoded_char == 2 {
24 pct_encoded_char = 0;
25 processing_pct_encoded = false;
26 } else {
27 pct_encoded_char += 1;
28
29 if c.is_ascii_hexdigit() {
30 continue;
31 } else {
32 return false;
33 }
34 }
35 }
36
37 if c == '%' {
38 processing_pct_encoded = true;
39 continue;
40 }
41
42 if is_unreserved(c) {
43 continue;
44 }
45
46 if is_sub_delim(c) {
47 continue;
48 }
49
50 if c == '@' {
56 continue;
57 }
58
59 match segment {
60 Segment::Base | Segment::Nz => {
61 if c == ':' {
62 continue;
63 }
64 }
65 Segment::NzNc => {}
66 }
67
68 return false;
69 }
70
71 true
72}
73
74fn is_unreserved(c: char) -> bool {
76 c.is_alphanumeric() || c == '-' || c == '.' || c == '_' || c == '~'
77}
78
79fn is_sub_delim(c: char) -> bool {
81 c == '!'
82 || c == '$'
83 || c == '&'
84 || c == '\''
85 || c == '('
86 || c == ')'
87 || c == '*'
88 || c == '+'
89 || c == ','
90 || c == ';'
91 || c == '='
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn test_segment_length() {
100 assert!(is_segment("", Segment::Base));
101 assert!(!is_segment("", Segment::Nz));
102 assert!(!is_segment("", Segment::NzNc));
103 }
104
105 #[test]
106 fn test_segment_alphanumeric() {
107 assert!(is_segment(
108 "abcdefghijklmnopqrstuvwxyz0123456789",
109 Segment::Base
110 ));
111 }
112
113 #[test]
114 fn test_segment_symbols() {
115 assert!(is_segment("!$&'()*+,;=@", Segment::Base));
116 assert!(is_segment("!$&'()*+,;=@", Segment::Nz));
117 assert!(is_segment("!$&'()*+,;=@", Segment::NzNc));
118 }
119
120 #[test]
121 fn test_segment_colon() {
122 assert!(is_segment(":", Segment::Base));
123 assert!(is_segment(":", Segment::Nz));
124 assert!(!is_segment(":", Segment::NzNc));
125 }
126
127 #[test]
128 fn test_segment_pct_encode() {
129 assert!(is_segment("%30%f9a", Segment::Base));
130 assert!(!is_segment("%3%f9a", Segment::Base));
131 assert!(!is_segment("%%f9a", Segment::Base));
132 }
133}