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
121
122
123
124
125
126
127
use regex::Regex;
use ferrum::{Request, Uri};
use ferrum::error::{HyperResult, HyperError};
use router::RouterInner;
use recognizer::{Recognizer, Params, ParamChunk};
pub trait UriFor {
fn generate(&self, glob_path: Option<&str>, recognizer: &Recognizer, params: Params) -> HyperResult<Uri>;
}
impl UriFor for Uri {
fn generate(&self, glob_path: Option<&str>, recognizer: &Recognizer, mut params: Params) -> HyperResult<Uri> {
if self != "*" {
let mut uri = String::new();
if let Some(scheme) = self.scheme() {
uri.push_str(scheme);
uri.push_str("://");
}
if let Some(authority) = self.authority() {
uri.push_str(authority);
}
let path = if let Some(path) = glob_path {
generate_for_glob(path, recognizer, &mut params)
} else {
generate_for_regex_captures(self.path(), &recognizer.glob_regex, &mut params)
};
if !path.is_empty() {
uri.push_str(&path);
}
if !params.is_empty() {
uri.push_str("?");
let count = params.len();
for (index, (ref key, ref value)) in params.into_iter().enumerate() {
uri.push_str(key);
uri.push_str("=");
uri.push_str(value);
if index < count - 1 {
uri.push_str("&");
}
}
}
uri.parse().map_err(HyperError::from)
} else {
Ok(self.clone())
}
}
}
pub fn uri_for(request: &Request, route_id: &str, params: Params) -> Uri {
let inner = request.extensions.get::<RouterInner>()
.expect("Couldn\'t find router set up properly.");
let (ref glob_path, ref recognizer) = *inner.route_ids.get(route_id)
.expect("No route with that ID");
match request.uri.generate(Some(glob_path), recognizer, params) {
Ok(uri) => uri,
Err(err) => panic!("New URI parse error: {:?}", err)
}
}
pub fn generate_for_glob(source: &str, recognizer: &Recognizer, params: &mut Params) -> String {
let mut replacements = vec![];
for &ParamChunk { ref name, start, end } in recognizer.param_chunks.iter() {
if let Some(replacement) = params.remove(name) {
replacements.push((start, end, replacement));
}
}
replace_params(source, replacements)
}
pub fn generate_for_regex_captures(source: &str, regex: &Regex, params: &mut Params) -> String {
let mut replacements = vec![];
if let Some(captures) = regex.captures(source) {
for capture_name in regex.capture_names() {
if let Some(name) = capture_name {
if let Some(replacement) = params.remove(name) {
if let Some(capture_match) = captures.name(name) {
replacements.push((capture_match.start(), capture_match.end(), replacement));
}
}
}
}
}
replace_params(source, replacements)
}
fn replace_params(source: &str, mut replacements: Vec<(usize, usize, String)>) -> String {
if !replacements.is_empty() {
replacements.sort_by(|a, b| a.0.cmp(&b.0));
let mut target = String::new();
let mut index = 0;
for (start, end, replacement) in replacements.into_iter() {
let source_chunk = &source.as_bytes()[index .. start];
target.push_str(&String::from_utf8_lossy(source_chunk));
target.push_str(&replacement);
index = end;
}
if index < source.len() {
target.push_str(&String::from_utf8_lossy(&source.as_bytes()[index ..]));
}
target
} else {
source.to_string()
}
}
#[cfg(test)]
mod tests;