agent_uri_dht/
endpoint.rs1use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub struct Endpoint {
20 protocol: String,
22 address: String,
24 path: Option<String>,
26}
27
28impl Endpoint {
29 #[must_use]
37 pub fn new(
38 protocol: impl Into<String>,
39 address: impl Into<String>,
40 path: Option<impl Into<String>>,
41 ) -> Self {
42 Self {
43 protocol: protocol.into(),
44 address: address.into(),
45 path: path.map(Into::into),
46 }
47 }
48
49 #[must_use]
51 pub fn https(address: impl Into<String>) -> Self {
52 Self::new("https", address, None::<String>)
53 }
54
55 #[must_use]
57 pub fn https_with_path(address: impl Into<String>, path: impl Into<String>) -> Self {
58 Self::new("https", address, Some(path))
59 }
60
61 #[must_use]
63 pub fn grpc(address: impl Into<String>) -> Self {
64 Self::new("grpc", address, None::<String>)
65 }
66
67 #[must_use]
69 pub fn websocket(address: impl Into<String>) -> Self {
70 Self::new("ws", address, None::<String>)
71 }
72
73 #[must_use]
75 pub fn protocol(&self) -> &str {
76 &self.protocol
77 }
78
79 #[must_use]
81 pub fn address(&self) -> &str {
82 &self.address
83 }
84
85 #[must_use]
87 pub fn path(&self) -> Option<&str> {
88 self.path.as_deref()
89 }
90
91 #[must_use]
93 pub fn to_uri(&self) -> String {
94 match &self.path {
95 Some(p) => format!("{}://{}{}", self.protocol, self.address, p),
96 None => format!("{}://{}", self.protocol, self.address),
97 }
98 }
99}
100
101impl fmt::Display for Endpoint {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(f, "{}", self.to_uri())
104 }
105}
106
107#[cfg(feature = "serde")]
108impl serde::Serialize for Endpoint {
109 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
110 where
111 S: serde::Serializer,
112 {
113 use serde::ser::SerializeStruct;
114 let mut state = serializer.serialize_struct("Endpoint", 3)?;
115 state.serialize_field("protocol", &self.protocol)?;
116 state.serialize_field("address", &self.address)?;
117 state.serialize_field("path", &self.path)?;
118 state.end()
119 }
120}
121
122#[cfg(feature = "serde")]
123impl<'de> serde::Deserialize<'de> for Endpoint {
124 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
125 where
126 D: serde::Deserializer<'de>,
127 {
128 #[derive(serde::Deserialize)]
129 struct EndpointData {
130 protocol: String,
131 address: String,
132 path: Option<String>,
133 }
134
135 let data = EndpointData::deserialize(deserializer)?;
136 Ok(Self {
137 protocol: data.protocol,
138 address: data.address,
139 path: data.path,
140 })
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn https_endpoint() {
150 let endpoint = Endpoint::https("agent.example.com:443");
151 assert_eq!(endpoint.protocol(), "https");
152 assert_eq!(endpoint.address(), "agent.example.com:443");
153 assert!(endpoint.path().is_none());
154 assert_eq!(endpoint.to_uri(), "https://agent.example.com:443");
155 }
156
157 #[test]
158 fn https_with_path_endpoint() {
159 let endpoint = Endpoint::https_with_path("agent.example.com:443", "/v1/agent");
160 assert_eq!(endpoint.protocol(), "https");
161 assert_eq!(endpoint.address(), "agent.example.com:443");
162 assert_eq!(endpoint.path(), Some("/v1/agent"));
163 assert_eq!(endpoint.to_uri(), "https://agent.example.com:443/v1/agent");
164 }
165
166 #[test]
167 fn grpc_endpoint() {
168 let endpoint = Endpoint::grpc("agent.example.com:50051");
169 assert_eq!(endpoint.protocol(), "grpc");
170 assert_eq!(endpoint.to_uri(), "grpc://agent.example.com:50051");
171 }
172
173 #[test]
174 fn websocket_endpoint() {
175 let endpoint = Endpoint::websocket("agent.example.com:8080");
176 assert_eq!(endpoint.protocol(), "ws");
177 assert_eq!(endpoint.to_uri(), "ws://agent.example.com:8080");
178 }
179
180 #[test]
181 fn custom_endpoint() {
182 let endpoint = Endpoint::new("mqtt", "broker.example.com:1883", Some("/agents"));
183 assert_eq!(endpoint.protocol(), "mqtt");
184 assert_eq!(endpoint.address(), "broker.example.com:1883");
185 assert_eq!(endpoint.path(), Some("/agents"));
186 assert_eq!(endpoint.to_uri(), "mqtt://broker.example.com:1883/agents");
187 }
188
189 #[test]
190 fn display_matches_to_uri() {
191 let endpoint = Endpoint::https("agent.example.com:443");
192 assert_eq!(format!("{endpoint}"), endpoint.to_uri());
193 }
194
195 #[test]
196 fn endpoints_are_equal() {
197 let endpoint1 = Endpoint::https("agent.example.com:443");
198 let endpoint2 = Endpoint::https("agent.example.com:443");
199 assert_eq!(endpoint1, endpoint2);
200 }
201
202 #[test]
203 fn different_endpoints_are_not_equal() {
204 let endpoint1 = Endpoint::https("agent1.example.com:443");
205 let endpoint2 = Endpoint::https("agent2.example.com:443");
206 assert_ne!(endpoint1, endpoint2);
207 }
208}