dev_kit/command/uri/
uri.rs1use crate::command::http_parser::HttpRequest;
2use crate::command::uri::{QueryPartName, QueryPartVal, Uri, UriComponent, UriComponentValue};
3use anyhow::anyhow;
4use itertools::Itertools;
5use percent_encoding::percent_decode_str;
6use std::collections::BTreeMap;
7use std::io::Read;
8use std::str::FromStr;
9
10impl FromStr for Uri {
11 type Err = anyhow::Error;
12
13 fn from_str(value: &str) -> Result<Self, Self::Err> {
14 if value.eq("-") {
15 let mut string = String::new();
16 let _ = std::io::stdin().lock().read_to_string(&mut string)
17 .map_err(|err| anyhow!("read from stdin failed, {}", err))?;
18 match string.trim() {
19 "-" => Err(anyhow!("Not a valid input")),
20 _ => Ok(Self::from_str(&string)?)
21 }
22 } else if let Ok(http_request) = HttpRequest::from_str(value) {
23 Ok(Uri::HttpRequest(http_request))
24 } else {
25 match url::Url::parse(&value) {
26 Ok(url) => Ok(Uri::Url(url)),
27 Err(_) => Ok(Uri::String(value.to_string())),
28 }
29 }
30 }
31}
32
33impl Uri {
34 pub fn decode(&self) -> crate::Result<String> {
35 match self {
36 Uri::HttpRequest(req) => {
37 let url = url::Url::try_from(req)?;
38 Ok(percent_decode_str(url.as_str()).decode_utf8()?.to_string())
39 }
40 Uri::Url(url) => {
41 Ok(percent_decode_str(url.as_str()).decode_utf8()?.to_string())
42 }
43 Uri::String(string) => {
44 Ok(percent_decode_str(string.as_str()).decode_utf8()?.to_string())
45 }
46 }
47 }
48
49 pub fn encode(&self) -> crate::Result<String> {
50 use percent_encoding::NON_ALPHANUMERIC as ascii_set;
51 match self {
52 Uri::HttpRequest(req) => {
53 let url = url::Url::try_from(req)?;
54 Ok(percent_encoding::utf8_percent_encode(url.as_str(), ascii_set).to_string())
55 }
56 Uri::Url(url) => {
57 Ok(percent_encoding::utf8_percent_encode(url.as_str(), ascii_set).to_string())
58 }
59 Uri::String(string) => {
60 Ok(percent_encoding::utf8_percent_encode(string.as_str(), ascii_set).to_string())
61 }
62 }
63 }
64
65 pub fn parse(&self, filter: &Option<Vec<UriComponent>>) -> crate::Result<Vec<UriComponentValue>> {
66 let url = url::Url::try_from(self)?;
67 let component_values = {
68 let schema = url.scheme().to_lowercase().to_string();
69 let components = vec![
70 UriComponentValue::Scheme(schema.clone()),
71 UriComponentValue::Authority({
72 let authority = url.authority().split("@").collect_vec();
73 if let [a, _] = authority.as_slice() {
74 Some(a.to_string())
75 } else {
76 None
77 }
78 }),
79 UriComponentValue::Host(url.host_str().unwrap_or_default().to_string()),
80 UriComponentValue::Port({
81 url.port().unwrap_or_else(|| {
82 match schema.as_str() {
83 "http" => 80,
84 "https" => 443,
85 _ => 0
86 }
87 })
88 }),
89 UriComponentValue::Path(url.path().to_string()),
90 UriComponentValue::Query({
91 let vals = url.query().and_then(|q|
92 serde_urlencoded::from_str::<Vec<(String, String)>>(q).ok()
93 ).unwrap_or_default();
94 let mut map = BTreeMap::<QueryPartName, QueryPartVal>::new();
95 for (name, value) in vals {
96 let name = QueryPartName(name.trim().to_string());
97 let value = QueryPartVal::Single(Some(value));
98 if let Some(exist) = map.get_mut(&name) {
99 *exist = exist.concat(&value);
100 } else {
101 map.insert(name, value);
102 }
103 }
104 map
105 })
106 ];
107 components
108 };
109 let result = if let Some(filter) = &filter {
110 component_values.into_iter().flat_map(|value| {
111 match value {
112 UriComponentValue::Scheme(_) => {
113 if filter.iter().any(|it| if let UriComponent::Scheme = it { true } else { false }) {
114 Some(value)
115 } else {
116 None
117 }
118 }
119 UriComponentValue::Authority(_) => {
120 if filter.iter().any(|it| if let UriComponent::Authority = it { true } else { false }) {
121 Some(value)
122 } else {
123 None
124 }
125 }
126 UriComponentValue::Host(_) => {
127 if filter.iter().any(|it| if let UriComponent::Host = it { true } else { false }) {
128 Some(value)
129 } else {
130 None
131 }
132 }
133 UriComponentValue::Port(_) => {
134 if filter.iter().any(|it| if let UriComponent::Port = it { true } else { false }) {
135 Some(value)
136 } else {
137 None
138 }
139 }
140 UriComponentValue::Path(_) => {
141 if filter.iter().any(|it| if let UriComponent::Path = it { true } else { false }) {
142 Some(value)
143 } else {
144 None
145 }
146 }
147 UriComponentValue::Query(parts) => {
148 let filter = filter.iter().filter(|&it| {
149 if let UriComponent::Query(_) = it { true } else { false }
150 }).collect_vec();
151 if filter.is_empty() {
152 None
153 } else {
154 let parts = parts.into_iter().filter(|(k, _)| {
155 filter.iter().any(|&filter| {
156 match filter {
157 UriComponent::Query(Some(filter)) => {
158 k.eq_ignore_ascii_case(filter)
159 }
160 UriComponent::Query(None) => {
161 true
162 }
163 _ => unreachable!()
164 }
165 })
166 }).collect_vec();
167 return Some(UriComponentValue::Query(parts.into_iter().collect::<BTreeMap<_, _>>()));
168 }
169 }
170 }
171 }).collect_vec()
172 } else {
173 component_values
174 };
175 Ok(result)
176 }
177}
178
179impl TryFrom<&Uri> for url::Url {
180 type Error = anyhow::Error;
181
182 fn try_from(uri: &Uri) -> Result<Self, Self::Error> {
183 match uri {
184 Uri::Url(url) => {
185 Ok(url.clone())
186 }
187 Uri::String(string) => {
188 Ok(url::Url::from_str(string)?)
189 }
190 Uri::HttpRequest(req) => {
191 Ok(url::Url::try_from(req).map_err(|_| anyhow!("Invalid http request"))?)
192 }
193 }
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200 #[test]
201 fn test_decode() {
202 let uri = Uri::from_str("passport%3Dabc%40sohu%2Dinc%2Ecom").unwrap();
203 assert_eq!(uri.decode().unwrap(), "passport=abc@sohu-inc.com");
204 }
205 #[test]
206 fn test_encode() {
207 let uri = Uri::from_str("passport=abc@sohu-inc.com").unwrap();
208 assert_eq!(uri.encode().unwrap(), "passport%3Dabc%40sohu%2Dinc%2Ecom");
209 }
210 #[test]
211 fn test_parse() {
212 let uri = Uri::from_str("https://abc:123@sohu.com/a/b/c?q1=1&q2=a&q2=b").unwrap();
213 let components = uri.parse(&None).unwrap();
214 for component in &components {
215 println!("{}=> {}", component.name(), component.string_value());
216 }
217 assert_eq!(components.len(), 6);
218 }
220
221 #[test]
222 fn test_parse_0() {
223 let uri = Uri::from_str("https://abc:123@sohu.com/a/b/c?q1=1&q2=a&q2=b").unwrap();
224 let components = uri.parse(
225 &Some(vec!["scheme", "host", "port", "?q1", "q2"].into_iter().flat_map(|it| UriComponent::from_str(it).ok()).collect_vec())
226 ).unwrap();
227 for component in &components {
228 println!("{}=> {}", component.name(), component.string_value());
229 }
230 assert_eq!(components.len(), 4);
231 }
232}