1use std::{
15 collections::HashMap, convert::TryFrom, hash::Hash, iter::FromIterator, str::FromStr, sync::Arc,
16};
17
18use zenoh_core::bail;
19
20use crate::split_once;
21
22pub const PROTO_SEPARATOR: char = '/';
24pub const METADATA_SEPARATOR: char = '?';
25pub const CONFIG_SEPARATOR: char = '#';
26pub const LIST_SEPARATOR: char = ';';
27pub const FIELD_SEPARATOR: char = '=';
28
29#[derive(Debug, Clone, Eq)]
30pub struct ArcProperties(pub Arc<HashMap<String, String>>);
31impl std::ops::Deref for ArcProperties {
32 type Target = Arc<HashMap<String, String>>;
33 fn deref(&self) -> &Self::Target {
34 &self.0
35 }
36}
37impl Hash for ArcProperties {
38 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
39 Arc::as_ptr(&self.0).hash(state);
40 }
41}
42impl std::ops::DerefMut for ArcProperties {
43 fn deref_mut(&mut self) -> &mut Self::Target {
44 &mut self.0
45 }
46}
47impl PartialEq for ArcProperties {
48 fn eq(&self, other: &Self) -> bool {
49 Arc::as_ptr(&self.0) == Arc::as_ptr(&other.0)
50 }
51}
52impl From<Arc<HashMap<String, String>>> for ArcProperties {
53 fn from(v: Arc<HashMap<String, String>>) -> Self {
54 ArcProperties(v)
55 }
56}
57impl From<HashMap<String, String>> for ArcProperties {
58 fn from(v: HashMap<String, String>) -> Self {
59 ArcProperties(Arc::new(v))
60 }
61}
62impl ArcProperties {
63 pub fn merge(&mut self, other: &Arc<HashMap<String, String>>) {
64 if other.is_empty() {
65 return;
66 }
67 if self.is_empty() {
68 self.0 = other.clone()
69 } else {
70 self.extend(other.iter().map(|(k, v)| (k.clone(), v.clone())))
71 }
72 }
73}
74impl FromStr for ArcProperties {
75 type Err = ();
76
77 fn from_str(s: &str) -> Result<Self, Self::Err> {
78 let this = HashMap::from_iter(s.split(LIST_SEPARATOR).filter_map(|s| {
79 let (k, v) = split_once(s, FIELD_SEPARATOR);
80 (!k.is_empty()).then(|| (k.to_owned(), v.to_owned()))
81 }));
82 match this.is_empty() {
83 true => Err(()),
84 false => Ok(this.into()),
85 }
86 }
87}
88impl Extend<(String, String)> for ArcProperties {
89 fn extend<T: IntoIterator<Item = (String, String)>>(&mut self, iter: T) {
90 let mut iter = iter.into_iter();
91 if let Some((k, v)) = iter.next() {
92 let (min, max) = iter.size_hint();
93 let extended = Arc::make_mut(&mut self.0);
94 extended.reserve(max.unwrap_or(min));
95 extended.insert(k, v);
96 for (k, v) in iter {
97 extended.insert(k, v);
98 }
99 };
100 }
101}
102
103#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
105#[serde(into = "String")]
106#[serde(try_from = "String")]
107pub struct Locator {
108 pub(crate) inner: String,
109 #[serde(skip)]
110 pub metadata: Option<ArcProperties>,
111}
112impl From<Locator> for String {
113 fn from(val: Locator) -> Self {
114 val.inner
115 }
116}
117impl core::fmt::Display for Locator {
118 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::fmt::Result {
119 f.write_str(&self.inner)?;
120 if let Some(meta) = &self.metadata {
121 let mut iter = meta.iter();
122 if let Some((k, v)) = iter.next() {
123 write!(f, "{}{}{}{}", METADATA_SEPARATOR, k, FIELD_SEPARATOR, v)?;
124 }
125 for (k, v) in iter {
126 write!(f, "{}{}{}{}", LIST_SEPARATOR, k, FIELD_SEPARATOR, v)?;
127 }
128 }
129 Ok(())
130 }
131}
132impl TryFrom<String> for Locator {
133 type Error = zenoh_core::Error;
134 fn try_from(mut inner: String) -> Result<Self, Self::Error> {
135 let (locator, meta) = split_once(&inner, METADATA_SEPARATOR);
136 if !locator.contains(PROTO_SEPARATOR) {
137 bail!("Missing protocol: locators must be of the form <proto>/<address>[?<metadata>]")
138 }
139 let metadata = meta.parse().ok();
140 let locator_len = locator.len();
141 inner.truncate(locator_len);
142 Ok(Locator { inner, metadata })
143 }
144}
145impl FromStr for Locator {
146 type Err = zenoh_core::Error;
147 fn from_str(s: &str) -> Result<Self, Self::Err> {
148 let (locator, meta) = split_once(s, METADATA_SEPARATOR);
149 if !locator.contains(PROTO_SEPARATOR) {
150 bail!("Missing protocol: locators must be of the form <proto>/<address>[?<metadata>]")
151 }
152 Ok(Locator {
153 inner: locator.to_owned(),
154 metadata: meta.parse().ok(),
155 })
156 }
157}
158
159impl Locator {
160 #[inline(always)]
161 pub fn new<Addr: std::fmt::Display>(protocol: &str, addr: &Addr) -> Self {
162 Locator::try_from(format!("{}{}{}", protocol, PROTO_SEPARATOR, addr)).unwrap()
163 }
164 #[must_use = "returns true if successful"]
165 pub fn set_addr(&mut self, addr: &str) -> bool {
166 let addr_start = self.inner.find(PROTO_SEPARATOR).unwrap() + 1;
167 let addr_end = self
168 .inner
169 .find(METADATA_SEPARATOR)
170 .unwrap_or(self.inner.len());
171 self.inner.replace_range(addr_start..addr_end, addr);
172 true
173 }
174}
175
176impl Locator {
177 pub fn split(
178 &self,
179 ) -> (
180 &str,
181 &str,
182 impl Iterator<Item = (&str, &str)> + DoubleEndedIterator + Clone,
183 ) {
184 let (protocol, rest) = split_once(&self.inner, PROTO_SEPARATOR);
185 let (address, properties) = split_once(rest, METADATA_SEPARATOR);
186 (
187 protocol,
188 address,
189 properties
190 .split(LIST_SEPARATOR)
191 .map(|prop| split_once(prop, FIELD_SEPARATOR)),
192 )
193 }
194
195 pub fn protocol(&self) -> &str {
196 let index = self.inner.find(PROTO_SEPARATOR).unwrap_or(self.inner.len());
197 &self.inner[..index]
198 }
199
200 pub fn address(&self) -> &str {
201 let index = self.inner.find(PROTO_SEPARATOR).unwrap_or(self.inner.len());
202 let rest = &self.inner[index + 1..];
203 let index = rest.find(METADATA_SEPARATOR).unwrap_or(rest.len());
204 &rest[..index]
205 }
206
207 pub fn clone_without_meta(&self) -> Self {
208 Locator {
209 inner: self.inner.clone(),
210 metadata: None,
211 }
212 }
213
214 pub fn metadata(&self) -> Option<&ArcProperties> {
215 self.metadata.as_ref()
216 }
217}
218
219pub(crate) trait HasCanonForm {
220 fn is_canon(&self) -> bool;
221 type Output;
222 fn canonicalize(self) -> Self::Output;
223}
224fn cmp(this: &str, than: &str) -> std::cmp::Ordering {
225 let is_longer = this.len().cmp(&than.len());
226 let this = this.chars();
227 let than = than.chars();
228 let zip = this.zip(than);
229 for (this, than) in zip {
230 match this.cmp(&than) {
231 std::cmp::Ordering::Equal => {}
232 o => return o,
233 }
234 }
235 is_longer
236}
237impl<'a, T: Iterator<Item = (&'a str, V)> + Clone, V> HasCanonForm for T {
238 fn is_canon(&self) -> bool {
239 let mut iter = self.clone();
240 let mut acc = if let Some((key, _)) = iter.next() {
241 key
242 } else {
243 return true;
244 };
245 for (key, _) in iter {
246 if cmp(key, acc) != std::cmp::Ordering::Greater {
247 return false;
248 }
249 acc = key;
250 }
251 true
252 }
253 type Output = Vec<(&'a str, V)>;
254 fn canonicalize(mut self) -> Self::Output {
255 let mut result = Vec::new();
256 if let Some(v) = self.next() {
257 result.push(v);
258 }
259 'outer: for (k, v) in self {
260 for (i, (x, _)) in result.iter().enumerate() {
261 match cmp(k, x) {
262 std::cmp::Ordering::Less => {
263 result.insert(i, (k, v));
264 continue 'outer;
265 }
266 std::cmp::Ordering::Equal => {
267 result[i].1 = v;
268 continue 'outer;
269 }
270 std::cmp::Ordering::Greater => {}
271 }
272 }
273 result.push((k, v))
274 }
275 result
276 }
277}
278
279#[test]
280fn locators() {
281 Locator::from_str("udp/127.0.0.1").unwrap();
282 let locator = Locator::from_str("udp/127.0.0.1?hello=there;general=kenobi").unwrap();
283 assert_eq!(locator.protocol(), "udp");
284 assert_eq!(locator.address(), "127.0.0.1");
285 assert_eq!(
286 ***locator.metadata().unwrap(),
287 [("general", "kenobi"), ("hello", "there")]
288 .iter()
289 .map(|&(k, v)| (k.to_owned(), v.to_owned()))
290 .collect::<HashMap<String, String>>()
291 );
292}
293
294pub type LocatorProtocol = str;