1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use regex::Regex;
use std::borrow::Cow;
use std::mem;
use std::str::Utf8Error;
lazy_static! {
static ref NON_URL: Regex = Regex::new(r"([^\w/\-]+|-{2,})").unwrap();
static ref START_DASHES: Regex = Regex::new(r"(^|/+)(?P<dash>-+)").unwrap();
static ref END_DASHES: Regex = Regex::new(r"(?P<dash>-+)($|/+)").unwrap();
}
#[inline]
fn percent_decode(input: &str) -> Result<Cow<str>, Utf8Error> {
use percent_encoding::percent_decode_str;
percent_decode_str(input).decode_utf8()
}
pub fn normalize_decode(name: &mut String) {
match percent_decode(&name) {
Ok(Cow::Borrowed(_)) => (),
Ok(Cow::Owned(mut decoded)) => mem::swap(name, &mut decoded),
Err(_) => warn!("Error decoding percent string"),
}
normalize(name);
}
pub fn normalize(name: &mut String) {
name.make_ascii_lowercase();
while let Some(mtch) = NON_URL.find(name) {
let start = mtch.start();
let end = mtch.end();
name.replace_range(start..end, "-");
}
let get_range = |captures: regex::Captures| {
let mtch = captures.name("dash").unwrap();
let start = mtch.start();
let end = mtch.end();
start..end
};
while let Some(captures) = START_DASHES.captures(name) {
let range = get_range(captures);
name.replace_range(range, "");
}
while let Some(captures) = END_DASHES.captures(name) {
let range = get_range(captures);
name.replace_range(range, "");
}
}
pub fn is_normal(name: &str) -> bool {
fn is_valid_char(ch: char) -> bool {
ch.is_ascii_lowercase()
|| ch.is_digit(10)
|| ch == ':'
|| ch == '-'
|| ch == '_'
|| ch == '/'
}
if !name.chars().all(is_valid_char) {
return false;
}
if NON_URL.find(name).is_some() {
return false;
}
if START_DASHES.find(name).is_some() {
return false;
}
if END_DASHES.find(name).is_some() {
return false;
}
true
}