dev_kit/command/uri/
uri.rs

1use 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        //assert_eq!(components[0].value, "https");
255    }
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}