1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use anyhow::{Context, Error};
/// QueryParam
#[derive(Clone, Debug, PartialEq)]
pub struct QueryParam {
/// The name of the query parameter
pub name: String,
/// Value of the query parameter
pub value: String,
}
/// URL struct
#[derive(Debug, Clone, PartialEq)]
pub struct Url {
/// Is url uses https
pub is_https: bool,
/// Host name
pub host: String,
/// Query parameters ([`QueryParam`]) in [`Vec`]
pub query_params: Vec<QueryParam>,
/// Port number
pub port: u16,
/// Paths
pub paths: Vec<String>,
}
impl Url {
/// Builds a URL from a string
/// * `url_string` - The URL string
/// ## Returns
/// [`Url`] if the URL was successfully parsed else [`Error`]
/// ## Example
/// ```rust
/// use menemen::url::Url;
/// let url = Url::build_from_string("https://behemehal.org/test?qtest=123".to_string()).unwrap();
///
/// assert_eq!(url.is_https, true);
/// assert_eq!(url.host, "behemehal.org".to_string());
/// assert_eq!(url.query_params.len(), 1);
/// assert_eq!(url.query_params[0].name, "qtest".to_string());
/// assert_eq!(url.query_params[0].value, "123".to_string());
/// assert_eq!(url.port, 443);
/// assert_eq!(url.paths.len(), 1);
/// assert_eq!(url.paths[0], "test".to_string());
/// ```
pub fn build_from_string(url: String) -> Result<Url, Error> {
let mut new_url = url.clone();
let protocol = url
.split("://")
.collect::<Vec<&str>>()
.first()
.with_context(|| "Failed to parse protocol")?
.to_string();
new_url = new_url.replace(&format!("{}://", protocol.as_str()), "");
let (host, port) = {
let _host = if new_url.contains("/") {
new_url.split("/").collect::<Vec<&str>>()[0].to_string()
} else {
new_url.clone()
};
let host = if _host.contains(":") {
_host.split(":").collect::<Vec<&str>>()[0].to_string()
} else {
_host.clone()
};
let _port = if _host.contains(":") {
_host.split(":").collect::<Vec<&str>>()[1].to_string()
} else if protocol == "https" {
"443".to_string()
} else {
"80".to_string()
};
let port = _port
.parse::<u16>()
.with_context(|| "Failed to parse port")?;
(host, port)
};
new_url = format!(
"/{}",
new_url.split("/").collect::<Vec<&str>>()[1..].join("/")
);
let paths = if new_url.contains("/") && new_url != "/" {
new_url.split("/").collect::<Vec<&str>>()[1..]
.iter()
.map(|s| {
if s.contains("?") {
s.to_string().split("?").collect::<Vec<&str>>()[0].to_string()
} else {
s.to_string()
}
})
.collect::<Vec<_>>()
} else {
vec![]
};
let query_params = if paths.len() == 0 {
vec![]
} else if new_url.contains("?") {
new_url = new_url.split("?").collect::<Vec<&str>>()[1].to_string();
new_url
.split("&")
.map(|x| {
let param = x.split("=").collect::<Vec<&str>>();
QueryParam {
name: param[0].to_string(),
value: if param.len() == 1 {
String::new()
} else {
param[1].to_string()
},
}
})
.collect::<Vec<QueryParam>>()
} else {
Vec::new()
};
Ok(Url {
is_https: protocol == "https",
host,
port,
paths,
query_params,
})
}
/// Join url parameters according to the url scheme
/// ## Returns
/// String of joined parameters
///
/// ## Example
/// ```
/// use menemen::url::Url;
/// let url = Url::build_from_string("https://behemehal.org/test?first=test&second=test".to_string()).unwrap();
/// let joiner_query_params = url.join_query_params();
/// assert_eq!(joiner_query_params, "first=test&second=test".to_string());
/// ```
pub fn join_query_params(&self) -> String {
self.query_params
.clone()
.into_iter()
.map(|x| format!("{}={}", x.name, x.value))
.collect::<Vec<String>>()
.join("&")
}
}