dev_kit/command/uri/
uri.rs

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