1use std::net::IpAddr;
4use std::str::FromStr;
5use std::ops::Deref;
6
7#[cfg(feature = "serde")]
8use serde::{Serialize, Deserialize, ser::Serializer, de::{Visitor, Deserializer, Error}};
9use url::{Url, PathSegmentsMut, ParseError};
10use thiserror::Error;
11
12pub mod host_details;
13pub use host_details::*;
14
15use crate::*;
16
17mod path_impl;
18pub use path_impl::*;
19mod domain_impl;
20pub use domain_impl::*;
21mod query_impl;
22pub use query_impl::*;
23
24#[derive(Clone)]
28pub struct BetterUrl {
29 url: Url,
31 host_details: Option<HostDetails>
33}
34
35#[derive(Debug, Error)]
37#[error("Failed to set the port.")]
38pub struct SetPortError;
39
40#[derive(Debug, Error)]
42#[error("Failed to set the host to an IP.")]
43pub struct SetIpHostError;
44
45#[derive(Debug, Error)]
47#[error("Failed to set the password.")]
48pub struct SetPasswordError;
49
50#[derive(Debug, Error)]
52#[error("Failed to set the username.")]
53pub struct SetUsernameError;
54
55#[derive(Debug, Error)]
57#[error("Failed to set the scheme.")]
58pub struct SetSchemeError;
59
60#[derive(Debug, Error)]
62#[error(transparent)]
63pub struct SetHostError(#[from] pub ParseError);
64
65impl BetterUrl {
66 pub fn parse(value: &str) -> Result<Self, <Self as FromStr>::Err> {
75 Self::from_str(value)
76 }
77
78 pub fn host_details(&self) -> Option<&HostDetails> {
95 self.host_details.as_ref()
96 }
97
98 pub fn domain_details(&self) -> Option<&DomainDetails> {
108 self.host_details()?.domain_details()
109 }
110
111 pub fn ipv4_details(&self) -> Option<&Ipv4Details> {
121 self.host_details()?.ipv4_details()
122 }
123
124 pub fn ipv6_details(&self) -> Option<&Ipv6Details> {
134 self.host_details()?.ipv6_details()
135 }
136
137 pub fn normalized_host(&self) -> Option<&str> {
139 let x = self.host_str()?;
140 let x = x.strip_prefix("www.").unwrap_or(x);
141 Some(x.strip_suffix(".").unwrap_or(x))
142 }
143
144 pub fn set_scheme(&mut self, scheme: &str) -> Result<(), SetSchemeError> {
148 self.url.set_scheme(scheme).map_err(|()| SetSchemeError)
149 }
150
151 pub fn set_username(&mut self, username: &str) -> Result<(), SetUsernameError> {
155 self.url.set_username(username).map_err(|()| SetUsernameError)
156 }
157
158 pub fn set_password(&mut self, password: Option<&str>) -> Result<(), SetPasswordError> {
162 self.url.set_password(password).map_err(|()| SetPasswordError)
163 }
164
165 fn set_host_with_known_details(&mut self, host: Option<&str>, host_details: Option<HostDetails>) -> Result<(), SetHostError> {
167 self.url.set_host(host)?;
168 self.host_details = host_details;
169 Ok(())
170 }
171
172 pub fn set_host(&mut self, host: Option<&str>) -> Result<(), SetHostError> {
176 self.url.set_host(host)?;
177 self.host_details = self.url.host().map(|host| HostDetails::from_host(&host));
178 Ok(())
179 }
180
181 pub fn set_ip_host(&mut self, address: IpAddr) -> Result<(), SetIpHostError> {
185 self.url.set_ip_host(address).map_err(|()| SetIpHostError)?;
186 self.host_details = Some(HostDetails::from_ip_addr(address));
187 Ok(())
188 }
189
190 pub fn set_port(&mut self, port: Option<u16>) -> Result<(), SetPortError> {
194 self.url.set_port(port).map_err(|()| SetPortError)
195 }
196
197 pub fn set_fragment(&mut self, fragment: Option<&str>) {
199 self.url.set_fragment(fragment)
200 }
201}
202
203impl std::fmt::Debug for BetterUrl {
204 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
205 write!(f, "{:?}", self.as_str())
206 }
207}
208
209impl std::fmt::Display for BetterUrl {
210 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211 self.url.fmt(formatter)
212 }
213}
214
215impl Deref for BetterUrl {
216 type Target = Url;
217
218 fn deref(&self) -> &Self::Target {
219 &self.url
220 }
221}
222
223
224impl PartialEq for BetterUrl {fn eq(&self, other: &Self) -> bool {self.url == other.url}}
225impl Eq for BetterUrl {}
226
227impl PartialEq<Url > for BetterUrl {fn eq(&self, other: &Url ) -> bool {&**self == other}}
228impl PartialEq<String > for BetterUrl {fn eq(&self, other: &String ) -> bool { self == &**other}}
229impl PartialEq<str > for BetterUrl {fn eq(&self, other: &str ) -> bool { self.as_str() == other}}
230impl PartialEq<&str > for BetterUrl {fn eq(&self, other: &&str ) -> bool { self == *other}}
231
232impl PartialEq<BetterUrl> for Url {fn eq(&self, other: &BetterUrl) -> bool {other == self}}
233impl PartialEq<BetterUrl> for String {fn eq(&self, other: &BetterUrl) -> bool {other == self}}
234impl PartialEq<BetterUrl> for str {fn eq(&self, other: &BetterUrl) -> bool {other == self}}
235impl PartialEq<BetterUrl> for &str {fn eq(&self, other: &BetterUrl) -> bool {other == self}}
236
237impl std::hash::Hash for BetterUrl {
238 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
240 std::hash::Hash::hash(&self.url, state)
241 }
242}
243
244impl PartialOrd for BetterUrl {
245 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
247 Some(self.cmp(other))
248 }
249}
250
251impl Ord for BetterUrl {
252 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
254 self.url.cmp(&other.url)
255 }
256}
257
258impl std::convert::AsRef<Url> for BetterUrl {
259 fn as_ref(&self) -> &Url {
260 &self.url
261 }
262}
263
264impl std::convert::AsRef<str> for BetterUrl {
265 fn as_ref(&self) -> &str {
266 self.url.as_ref()
267 }
268}
269
270impl FromStr for BetterUrl {
271 type Err = <Url as FromStr>::Err;
272
273 fn from_str(s: &str) -> Result<Self, Self::Err> {
274 Url::from_str(s).map(Into::into)
275 }
276}
277
278impl TryFrom<&str> for BetterUrl {
279 type Error = <Self as FromStr>::Err;
280
281 fn try_from(value: &str) -> Result<Self, Self::Error> {
282 Self::from_str(value)
283 }
284}
285
286impl From<Url> for BetterUrl {
287 fn from(value: Url) -> Self {
288 Self {
289 host_details: HostDetails::from_url(&value),
290 url: value
291 }
292 }
293}
294
295impl From<BetterUrl> for Url {
296 fn from(value: BetterUrl) -> Self {
297 value.url
298 }
299}
300
301impl From<BetterUrl> for String {
302 fn from(value: BetterUrl) -> Self {
303 value.url.into()
304 }
305}
306
307#[cfg(feature = "serde")]
309struct BetterUrlVisitor;
310
311#[cfg(feature = "serde")]
312impl<'de> Visitor<'de> for BetterUrlVisitor {
313 type Value = BetterUrl;
314
315 fn visit_str<E: Error>(self, s: &str) -> Result<Self::Value, E> {
316 s.try_into().map_err(E::custom)
317 }
318
319 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320 write!(formatter, "Expected a string")
321 }
322}
323
324#[cfg(feature = "serde")]
325impl<'de> Deserialize<'de> for BetterUrl {
326 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
327 deserializer.deserialize_any(BetterUrlVisitor)
328 }
329}
330
331#[cfg(feature = "serde")]
332impl Serialize for BetterUrl {
333 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
334 serializer.serialize_str(self.as_str())
335 }
336}