dynamo_runtime/
protocols.rs1use serde::{Deserialize, Serialize};
5use std::str::FromStr;
6
7pub mod annotated;
8pub mod maybe_error;
9
10pub type LeaseId = i64;
11
12const DEFAULT_NAMESPACE: &str = "NS";
14
15const DEFAULT_COMPONENT: &str = "C";
16
17const DEFAULT_ENDPOINT: &str = "E";
18
19pub const ENDPOINT_SCHEME: &str = "dyn://";
23
24#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
25pub struct Component {
26 pub name: String,
27 pub namespace: String,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
40pub struct EndpointId {
41 pub namespace: String,
42 pub component: String,
43 pub name: String,
44}
45
46impl PartialEq<Vec<&str>> for EndpointId {
47 fn eq(&self, other: &Vec<&str>) -> bool {
48 if other.len() != 3 {
49 return false;
50 }
51
52 self.namespace == other[0] && self.component == other[1] && self.name == other[2]
53 }
54}
55
56impl PartialEq<[&str; 3]> for EndpointId {
57 fn eq(&self, other: &[&str; 3]) -> bool {
58 self.namespace == other[0] && self.component == other[1] && self.name == other[2]
59 }
60}
61
62impl PartialEq<EndpointId> for [&str; 3] {
63 fn eq(&self, other: &EndpointId) -> bool {
64 other == self
65 }
66}
67
68impl PartialEq<EndpointId> for Vec<&str> {
69 fn eq(&self, other: &EndpointId) -> bool {
70 other == self
71 }
72}
73
74impl Default for EndpointId {
75 fn default() -> Self {
76 EndpointId {
77 namespace: DEFAULT_NAMESPACE.to_string(),
78 component: DEFAULT_COMPONENT.to_string(),
79 name: DEFAULT_ENDPOINT.to_string(),
80 }
81 }
82}
83
84impl From<&str> for EndpointId {
85 fn from(s: &str) -> Self {
111 let input = s.strip_prefix(ENDPOINT_SCHEME).unwrap_or(s);
112
113 let mut parts = input
115 .trim_matches([' ', '/', '.'])
116 .split(['.', '/'])
117 .filter(|x| !x.is_empty());
118
119 let p1 = parts.next();
121 let p2 = parts.next();
122 let p3 = parts.next();
123
124 let namespace;
125 let component;
126 let name;
127
128 match (p1, p2, p3) {
129 (None, _, _) => {
130 namespace = DEFAULT_NAMESPACE.to_string();
133 component = DEFAULT_COMPONENT.to_string();
134 name = DEFAULT_ENDPOINT.to_string();
135 }
136 (Some(c), None, _) => {
137 namespace = DEFAULT_NAMESPACE.to_string();
138 component = c.to_string();
139 name = DEFAULT_ENDPOINT.to_string();
140 }
141 (Some(ns), Some(c), None) => {
142 namespace = ns.to_string();
144 component = c.to_string();
145 name = DEFAULT_ENDPOINT.to_string();
146 }
147 (Some(ns), Some(c), Some(ep)) => {
148 namespace = ns.to_string();
149 component = c.to_string();
150
151 let mut endpoint_buf = String::from(ep); for part in parts {
155 endpoint_buf.push('_');
157 endpoint_buf.push_str(part);
158 }
159 name = endpoint_buf;
160 }
161 }
162
163 EndpointId {
164 namespace,
165 component,
166 name,
167 }
168 }
169}
170
171impl FromStr for EndpointId {
172 type Err = core::convert::Infallible;
173
174 fn from_str(s: &str) -> Result<Self, Self::Err> {
195 Ok(EndpointId::from(s))
196 }
197}
198
199impl EndpointId {
200 pub fn as_url(&self) -> String {
202 format!(
203 "{ENDPOINT_SCHEME}{}.{}.{}",
204 self.namespace, self.component, self.name
205 )
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212 use std::convert::TryFrom;
213 use std::str::FromStr;
214
215 #[test]
216 fn test_valid_endpoint_from() {
217 let input = "namespace1/component1/endpoint1";
218 let endpoint = EndpointId::from(input);
219
220 assert_eq!(endpoint.namespace, "namespace1");
221 assert_eq!(endpoint.component, "component1");
222 assert_eq!(endpoint.name, "endpoint1");
223 }
224
225 #[test]
226 fn test_valid_endpoint_from_str() {
227 let input = "namespace2/component2/endpoint2";
228 let endpoint = EndpointId::from_str(input).unwrap();
229
230 assert_eq!(endpoint.namespace, "namespace2");
231 assert_eq!(endpoint.component, "component2");
232 assert_eq!(endpoint.name, "endpoint2");
233 }
234
235 #[test]
236 fn test_valid_endpoint_parse() {
237 let input = "namespace3/component3/endpoint3";
238 let endpoint: EndpointId = input.parse().unwrap();
239
240 assert_eq!(endpoint.namespace, "namespace3");
241 assert_eq!(endpoint.component, "component3");
242 assert_eq!(endpoint.name, "endpoint3");
243 }
244
245 #[test]
246 fn test_endpoint_from() {
247 let result = EndpointId::from("component");
248 assert_eq!(
249 result,
250 vec![DEFAULT_NAMESPACE, "component", DEFAULT_ENDPOINT]
251 );
252 }
253
254 #[test]
255 fn test_namespace_component_endpoint() {
256 let result = EndpointId::from("namespace.component.endpoint");
257 assert_eq!(result, vec!["namespace", "component", "endpoint"]);
258 }
259
260 #[test]
261 fn test_forward_slash_separator() {
262 let result = EndpointId::from("namespace/component");
263 assert_eq!(result, vec!["namespace", "component", DEFAULT_ENDPOINT]);
264 }
265
266 #[test]
267 fn test_multiple_parts() {
268 let result = EndpointId::from("namespace.component.endpoint.other.parts");
269 assert_eq!(
270 result,
271 vec!["namespace", "component", "endpoint_other_parts"]
272 );
273 }
274
275 #[test]
276 fn test_mixed_separators() {
277 let result: EndpointId = "namespace/component.endpoint".into();
279 assert_eq!(result, vec!["namespace", "component", "endpoint"]);
280 }
281
282 #[test]
283 fn test_empty_string() {
284 let result = EndpointId::from("");
285 assert_eq!(
286 result,
287 vec![DEFAULT_NAMESPACE, DEFAULT_COMPONENT, DEFAULT_ENDPOINT]
288 );
289
290 let result = EndpointId::from(" ");
292 assert_eq!(
293 result,
294 vec![DEFAULT_NAMESPACE, DEFAULT_COMPONENT, DEFAULT_ENDPOINT]
295 );
296 }
297
298 #[test]
299 fn test_parse_with_scheme_and_url_roundtrip() {
300 let input = "dyn://ns/cp/ep";
301 let endpoint: EndpointId = input.parse().unwrap();
302 assert_eq!(endpoint, vec!["ns", "cp", "ep"]);
303 assert_eq!(endpoint.as_url(), "dyn://ns.cp.ep");
304 }
305}