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