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 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 }
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}