1use crate::RayError;
2use std::fmt;
3use url::Url;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct RayUrl(Url);
20
21impl RayUrl {
22 pub fn parse(input: impl AsRef<str>) -> Result<Self, RayError> {
26 Self::try_from(input.as_ref())
27 }
28
29 pub fn as_str(&self) -> &str {
31 self.0.as_str()
32 }
33}
34
35impl AsRef<str> for RayUrl {
36 fn as_ref(&self) -> &str {
37 self.0.as_str()
38 }
39}
40
41impl fmt::Display for RayUrl {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 self.0.fmt(f)
44 }
45}
46
47impl TryFrom<&str> for RayUrl {
48 type Error = RayError;
49
50 fn try_from(input: &str) -> Result<Self, Self::Error> {
51 let trimmed = input.trim();
52 let url = match Url::parse(trimmed) {
53 Ok(url) => url,
54 Err(err) => {
55 if trimmed.contains("://") {
56 return Err(RayError::invalid_input(
57 "url",
58 format!("invalid URL `{}`: {}", trimmed, err),
59 ));
60 }
61
62 let candidate = format!("https://{}", trimmed);
63 Url::parse(&candidate).map_err(|_| {
64 RayError::invalid_input("url", format!("invalid URL `{}`: {}", trimmed, err))
65 })?
66 }
67 };
68 Ok(Self(url))
69 }
70}
71
72impl TryFrom<String> for RayUrl {
73 type Error = RayError;
74
75 fn try_from(input: String) -> Result<Self, Self::Error> {
76 Self::try_from(input.as_str())
77 }
78}
79
80impl From<Url> for RayUrl {
81 fn from(url: Url) -> Self {
82 Self(url)
83 }
84}
85
86pub trait RayUrlInput {
90 fn try_into_ray_url(self) -> Result<RayUrl, RayError>;
92}
93
94impl RayUrlInput for RayUrl {
95 fn try_into_ray_url(self) -> Result<RayUrl, RayError> {
96 Ok(self)
97 }
98}
99
100impl RayUrlInput for Url {
101 fn try_into_ray_url(self) -> Result<RayUrl, RayError> {
102 Ok(RayUrl(self))
103 }
104}
105
106impl RayUrlInput for &RayUrl {
107 fn try_into_ray_url(self) -> Result<RayUrl, RayError> {
108 Ok(self.clone())
109 }
110}
111
112impl RayUrlInput for &Url {
113 fn try_into_ray_url(self) -> Result<RayUrl, RayError> {
114 Ok(RayUrl(self.clone()))
115 }
116}
117
118impl RayUrlInput for &str {
119 fn try_into_ray_url(self) -> Result<RayUrl, RayError> {
120 RayUrl::try_from(self)
121 }
122}
123
124impl RayUrlInput for String {
125 fn try_into_ray_url(self) -> Result<RayUrl, RayError> {
126 RayUrl::try_from(self)
127 }
128}
129
130impl RayUrlInput for &String {
131 fn try_into_ray_url(self) -> Result<RayUrl, RayError> {
132 RayUrl::try_from(self.as_str())
133 }
134}