trillium_client/
into_url.rs1use crate::{Error, Result};
2use std::{
3 net::{IpAddr, SocketAddr},
4 str::FromStr,
5};
6use trillium_server_common::url::{ParseError, Url};
7
8pub trait IntoUrl {
10 fn into_url(self, base: Option<&Url>) -> Result<Url>;
12}
13
14impl IntoUrl for Url {
15 fn into_url(self, base: Option<&Url>) -> Result<Url> {
16 if self.cannot_be_a_base() {
17 return Err(Error::UnexpectedUriFormat);
18 }
19
20 if base.is_some_and(|base| !self.as_str().starts_with(base.as_str())) {
21 Err(Error::UnexpectedUriFormat)
22 } else {
23 Ok(self)
24 }
25 }
26}
27
28impl IntoUrl for &str {
29 fn into_url(self, base: Option<&Url>) -> Result<Url> {
30 match (Url::from_str(self), base) {
31 (Ok(url), base) => url.into_url(base),
32 (Err(ParseError::RelativeUrlWithoutBase), Some(base)) => base
33 .join(self.trim_start_matches('/'))
34 .map_err(|_| Error::UnexpectedUriFormat),
35 _ => Err(Error::UnexpectedUriFormat),
36 }
37 }
38}
39
40impl IntoUrl for String {
41 #[inline(always)]
42 fn into_url(self, base: Option<&Url>) -> Result<Url> {
43 self.as_str().into_url(base)
44 }
45}
46
47impl<S: AsRef<str>> IntoUrl for &[S] {
48 fn into_url(self, base: Option<&Url>) -> Result<Url> {
49 let Some(mut url) = base.cloned() else {
50 return Err(Error::UnexpectedUriFormat);
51 };
52 url.path_segments_mut()
53 .map_err(|_| Error::UnexpectedUriFormat)?
54 .pop_if_empty()
55 .extend(self);
56 Ok(url)
57 }
58}
59
60impl<S: AsRef<str>, const N: usize> IntoUrl for [S; N] {
61 fn into_url(self, base: Option<&Url>) -> Result<Url> {
62 self.as_slice().into_url(base)
63 }
64}
65
66impl<S: AsRef<str>> IntoUrl for Vec<S> {
67 fn into_url(self, base: Option<&Url>) -> Result<Url> {
68 self.as_slice().into_url(base)
69 }
70}
71
72impl IntoUrl for SocketAddr {
73 fn into_url(self, base: Option<&Url>) -> Result<Url> {
74 let scheme = if self.port() == 443 { "https" } else { "http" };
75 format!("{scheme}://{self}").into_url(base)
76 }
77}
78
79impl IntoUrl for IpAddr {
80 fn into_url(self, base: Option<&Url>) -> Result<Url> {
82 match self {
83 IpAddr::V4(v4) => format!("http://{v4}"),
84 IpAddr::V6(v6) => format!("http://[{v6}]"),
85 }
86 .into_url(base)
87 }
88}