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