1use std::{fmt::Display, str::FromStr};
4
5use serde::{Deserialize, Serialize};
6
7use crate::ConnectionSecurity;
8#[cfg(doc)]
9use crate::NetHsm;
10
11#[derive(Debug, thiserror::Error)]
13pub enum Error {
14 #[error("The format of URL {url} is invalid because {context}")]
19 UrlInvalidFormat {
20 url: url::Url,
22
23 context: &'static str,
27 },
28
29 #[error("URL parser error:\n{0}")]
31 UrlParse(#[from] url::ParseError),
32}
33
34#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
38pub struct Connection {
39 pub(crate) url: Url,
40 pub(crate) tls_security: ConnectionSecurity,
41}
42
43impl Connection {
44 pub fn new(url: Url, tls_security: ConnectionSecurity) -> Self {
46 Self { url, tls_security }
47 }
48
49 pub fn url(&self) -> &Url {
51 &self.url
52 }
53
54 pub fn tls_security(&self) -> &ConnectionSecurity {
56 &self.tls_security
57 }
58}
59
60#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
69#[serde(try_from = "String")]
70pub struct Url(url::Url);
71
72impl Url {
73 pub fn new(url: &str) -> Result<Self, crate::Error> {
100 let url = url::Url::parse(url).map_err(Error::UrlParse)?;
101 if !url.scheme().eq("https") {
102 Err(Error::UrlInvalidFormat {
103 url,
104 context: "a URL must use TLS",
105 }
106 .into())
107 } else if !url.has_host() {
108 Err(Error::UrlInvalidFormat {
109 url,
110 context: "a URL must have a host component",
111 }
112 .into())
113 } else if url.password().is_some() {
114 Err(Error::UrlInvalidFormat {
115 url,
116 context: "a URL must not have a password component",
117 }
118 .into())
119 } else if !url.username().is_empty() {
120 Err(Error::UrlInvalidFormat {
121 url,
122 context: "a URL must not have a user component",
123 }
124 .into())
125 } else if url.query().is_some() {
126 Err(Error::UrlInvalidFormat {
127 url,
128 context: "a URL must not have a query component",
129 }
130 .into())
131 } else {
132 Ok(Self(url))
133 }
134 }
135}
136
137impl Display for Url {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 write!(f, "{}", self.0)
140 }
141}
142
143impl TryFrom<&str> for Url {
144 type Error = crate::Error;
145
146 fn try_from(value: &str) -> Result<Self, crate::Error> {
147 Self::new(value)
148 }
149}
150
151impl TryFrom<String> for Url {
152 type Error = crate::Error;
153
154 fn try_from(value: String) -> Result<Self, crate::Error> {
155 Self::new(&value)
156 }
157}
158
159impl FromStr for Url {
160 type Err = crate::Error;
161
162 fn from_str(s: &str) -> Result<Self, Self::Err> {
163 Self::new(s)
164 }
165}