zenoh_protocol/core/
endpoint.rs

1//
2// Copyright (c) 2023 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14use alloc::{borrow::ToOwned, format, string::String};
15use core::{borrow::Borrow, convert::TryFrom, fmt, str::FromStr};
16
17use zenoh_result::{bail, zerror, Error as ZError, ZResult};
18
19use super::{locator::*, parameters};
20
21// Parsing chars
22pub const PROTO_SEPARATOR: char = '/';
23pub const METADATA_SEPARATOR: char = '?';
24pub const CONFIG_SEPARATOR: char = '#';
25
26// Parsing functions
27pub(super) fn protocol(s: &str) -> &str {
28    let pdix = s.find(PROTO_SEPARATOR).unwrap_or(s.len());
29    &s[..pdix]
30}
31
32pub(super) fn address(s: &str) -> &str {
33    let pdix = s.find(PROTO_SEPARATOR).unwrap_or(s.len());
34    let midx = s.find(METADATA_SEPARATOR).unwrap_or(s.len());
35    let cidx = s.find(CONFIG_SEPARATOR).unwrap_or(s.len());
36    &s[pdix + 1..midx.min(cidx)]
37}
38
39pub(super) fn metadata(s: &str) -> &str {
40    match s.find(METADATA_SEPARATOR) {
41        Some(midx) => {
42            let cidx = s.find(CONFIG_SEPARATOR).unwrap_or(s.len());
43            &s[midx + 1..cidx]
44        }
45        None => "",
46    }
47}
48
49pub(super) fn config(s: &str) -> &str {
50    match s.find(CONFIG_SEPARATOR) {
51        Some(cidx) => &s[cidx + 1..],
52        None => "",
53    }
54}
55
56// Protocol
57#[repr(transparent)]
58#[derive(Copy, Clone, PartialEq, Eq, Hash)]
59pub struct Protocol<'a>(pub(super) &'a str);
60
61impl<'a> Protocol<'a> {
62    pub fn as_str(&self) -> &'a str {
63        self.0
64    }
65}
66
67impl AsRef<str> for Protocol<'_> {
68    fn as_ref(&self) -> &str {
69        self.as_str()
70    }
71}
72
73impl fmt::Display for Protocol<'_> {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        f.write_str(self.as_str())
76    }
77}
78
79impl fmt::Debug for Protocol<'_> {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        write!(f, "{self}")
82    }
83}
84
85#[repr(transparent)]
86#[derive(PartialEq, Eq, Hash)]
87pub struct ProtocolMut<'a>(&'a mut EndPoint);
88
89impl<'a> ProtocolMut<'a> {
90    pub fn as_str(&'a self) -> &'a str {
91        address(self.0.as_str())
92    }
93
94    pub fn set(&mut self, p: &str) -> ZResult<()> {
95        let ep = EndPoint::new(p, self.0.address(), self.0.metadata(), self.0.config())?;
96
97        self.0.inner = ep.inner;
98        Ok(())
99    }
100}
101
102impl AsRef<str> for ProtocolMut<'_> {
103    fn as_ref(&self) -> &str {
104        self.as_str()
105    }
106}
107
108impl fmt::Display for ProtocolMut<'_> {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        f.write_str(self.as_str())
111    }
112}
113
114impl fmt::Debug for ProtocolMut<'_> {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        write!(f, "{self}")
117    }
118}
119
120// Address
121#[repr(transparent)]
122#[derive(Copy, Clone, PartialEq, Eq, Hash)]
123pub struct Address<'a>(pub(super) &'a str);
124
125impl<'a> Address<'a> {
126    pub fn as_str(&self) -> &'a str {
127        self.0
128    }
129}
130
131impl AsRef<str> for Address<'_> {
132    fn as_ref(&self) -> &str {
133        self.as_str()
134    }
135}
136
137impl fmt::Display for Address<'_> {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        f.write_str(self.as_str())
140    }
141}
142
143impl fmt::Debug for Address<'_> {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        write!(f, "{self}")
146    }
147}
148
149impl<'a> From<&'a str> for Address<'a> {
150    fn from(value: &'a str) -> Self {
151        Address(value)
152    }
153}
154
155#[repr(transparent)]
156#[derive(PartialEq, Eq, Hash)]
157pub struct AddressMut<'a>(&'a mut EndPoint);
158
159impl<'a> AddressMut<'a> {
160    pub fn as_str(&'a self) -> &'a str {
161        address(self.0.as_str())
162    }
163
164    pub fn set(&'a mut self, a: &str) -> ZResult<()> {
165        let ep = EndPoint::new(self.0.protocol(), a, self.0.metadata(), self.0.config())?;
166
167        self.0.inner = ep.inner;
168        Ok(())
169    }
170}
171
172impl AsRef<str> for AddressMut<'_> {
173    fn as_ref(&self) -> &str {
174        self.as_str()
175    }
176}
177
178impl fmt::Display for AddressMut<'_> {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        f.write_str(self.as_str())
181    }
182}
183
184impl fmt::Debug for AddressMut<'_> {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        write!(f, "{self}")
187    }
188}
189
190// Metadata
191#[repr(transparent)]
192#[derive(Copy, Clone, PartialEq, Eq, Hash)]
193pub struct Metadata<'a>(pub(super) &'a str);
194
195impl<'a> Metadata<'a> {
196    pub const RELIABILITY: &'static str = "rel";
197    pub const PRIORITIES: &'static str = "prio";
198
199    pub fn as_str(&self) -> &'a str {
200        self.0
201    }
202
203    pub fn is_empty(&'a self) -> bool {
204        self.as_str().is_empty()
205    }
206
207    pub fn iter(&'a self) -> impl DoubleEndedIterator<Item = (&'a str, &'a str)> + Clone {
208        parameters::iter(self.0)
209    }
210
211    pub fn get(&'a self, k: &str) -> Option<&'a str> {
212        parameters::get(self.0, k)
213    }
214
215    pub fn values(&'a self, k: &str) -> impl DoubleEndedIterator<Item = &'a str> {
216        parameters::values(self.0, k)
217    }
218}
219
220impl AsRef<str> for Metadata<'_> {
221    fn as_ref(&self) -> &str {
222        self.as_str()
223    }
224}
225
226impl fmt::Display for Metadata<'_> {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        f.write_str(self.as_str())
229    }
230}
231
232impl fmt::Debug for Metadata<'_> {
233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234        write!(f, "{self}")
235    }
236}
237
238#[repr(transparent)]
239#[derive(PartialEq, Eq, Hash)]
240pub struct MetadataMut<'a>(&'a mut EndPoint);
241
242impl<'a> MetadataMut<'a> {
243    pub fn as_str(&'a self) -> &'a str {
244        metadata(self.0.as_str())
245    }
246
247    pub fn is_empty(&'a self) -> bool {
248        self.as_str().is_empty()
249    }
250}
251
252impl MetadataMut<'_> {
253    pub fn extend_from_iter<'s, I, K, V>(&mut self, iter: I) -> ZResult<()>
254    where
255        I: Iterator<Item = (&'s K, &'s V)> + Clone,
256        K: Borrow<str> + 's + ?Sized,
257        V: Borrow<str> + 's + ?Sized,
258    {
259        let ep = EndPoint::new(
260            self.0.protocol(),
261            self.0.address(),
262            parameters::from_iter(parameters::sort(parameters::join(
263                self.0.metadata().iter(),
264                iter.map(|(k, v)| (k.borrow(), v.borrow())),
265            ))),
266            self.0.config(),
267        )?;
268
269        self.0.inner = ep.inner;
270        Ok(())
271    }
272
273    pub fn insert<K, V>(&mut self, k: K, v: V) -> ZResult<()>
274    where
275        K: Borrow<str>,
276        V: Borrow<str>,
277    {
278        let ep = EndPoint::new(
279            self.0.protocol(),
280            self.0.address(),
281            parameters::insert_sort(self.0.metadata().as_str(), k.borrow(), v.borrow()).0,
282            self.0.config(),
283        )?;
284
285        self.0.inner = ep.inner;
286        Ok(())
287    }
288
289    pub fn remove<K>(&mut self, k: K) -> ZResult<()>
290    where
291        K: Borrow<str>,
292    {
293        let ep = EndPoint::new(
294            self.0.protocol(),
295            self.0.address(),
296            parameters::remove(self.0.metadata().as_str(), k.borrow()).0,
297            self.0.config(),
298        )?;
299
300        self.0.inner = ep.inner;
301        Ok(())
302    }
303}
304
305impl AsRef<str> for MetadataMut<'_> {
306    fn as_ref(&self) -> &str {
307        self.as_str()
308    }
309}
310
311impl fmt::Display for MetadataMut<'_> {
312    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313        f.write_str(self.as_str())
314    }
315}
316
317impl fmt::Debug for MetadataMut<'_> {
318    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319        write!(f, "{self}")
320    }
321}
322
323// Config
324#[repr(transparent)]
325#[derive(Copy, Clone, PartialEq, Eq, Hash)]
326pub struct Config<'a>(pub(super) &'a str);
327
328impl<'a> Config<'a> {
329    pub fn as_str(&self) -> &'a str {
330        self.0
331    }
332
333    pub fn is_empty(&'a self) -> bool {
334        self.as_str().is_empty()
335    }
336
337    pub fn iter(&'a self) -> impl DoubleEndedIterator<Item = (&'a str, &'a str)> + Clone {
338        parameters::iter(self.0)
339    }
340
341    pub fn get(&'a self, k: &str) -> Option<&'a str> {
342        parameters::get(self.0, k)
343    }
344
345    pub fn values(&'a self, k: &str) -> impl DoubleEndedIterator<Item = &'a str> {
346        parameters::values(self.0, k)
347    }
348}
349
350impl AsRef<str> for Config<'_> {
351    fn as_ref(&self) -> &str {
352        self.as_str()
353    }
354}
355
356impl fmt::Display for Config<'_> {
357    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358        f.write_str(self.as_str())
359    }
360}
361
362impl fmt::Debug for Config<'_> {
363    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364        write!(f, "{self}")
365    }
366}
367
368#[repr(transparent)]
369#[derive(PartialEq, Eq, Hash)]
370pub struct ConfigMut<'a>(&'a mut EndPoint);
371
372impl<'a> ConfigMut<'a> {
373    pub fn as_str(&'a self) -> &'a str {
374        config(self.0.as_str())
375    }
376
377    pub fn is_empty(&'a self) -> bool {
378        self.as_str().is_empty()
379    }
380}
381
382impl ConfigMut<'_> {
383    pub fn extend_from_iter<'s, I, K, V>(&mut self, iter: I) -> ZResult<()>
384    where
385        I: Iterator<Item = (&'s K, &'s V)> + Clone,
386        K: Borrow<str> + 's + ?Sized,
387        V: Borrow<str> + 's + ?Sized,
388    {
389        let ep = EndPoint::new(
390            self.0.protocol(),
391            self.0.address(),
392            self.0.metadata(),
393            parameters::from_iter(parameters::sort(parameters::join(
394                self.0.config().iter(),
395                iter.map(|(k, v)| (k.borrow(), v.borrow())),
396            ))),
397        )?;
398
399        self.0.inner = ep.inner;
400        Ok(())
401    }
402
403    pub fn insert<K, V>(&mut self, k: K, v: V) -> ZResult<()>
404    where
405        K: Borrow<str>,
406        V: Borrow<str>,
407    {
408        let ep = EndPoint::new(
409            self.0.protocol(),
410            self.0.address(),
411            self.0.metadata(),
412            parameters::insert_sort(self.0.config().as_str(), k.borrow(), v.borrow()).0,
413        )?;
414
415        self.0.inner = ep.inner;
416        Ok(())
417    }
418
419    pub fn remove<K>(&mut self, k: K) -> ZResult<()>
420    where
421        K: Borrow<str>,
422    {
423        let ep = EndPoint::new(
424            self.0.protocol(),
425            self.0.address(),
426            self.0.metadata(),
427            parameters::remove(self.0.config().as_str(), k.borrow()).0,
428        )?;
429
430        self.0.inner = ep.inner;
431        Ok(())
432    }
433}
434
435impl AsRef<str> for ConfigMut<'_> {
436    fn as_ref(&self) -> &str {
437        self.as_str()
438    }
439}
440
441impl fmt::Display for ConfigMut<'_> {
442    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443        f.write_str(self.as_str())
444    }
445}
446
447impl fmt::Debug for ConfigMut<'_> {
448    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449        write!(f, "{self}")
450    }
451}
452
453/// A string that respects the [`EndPoint`] canon form: `<locator>[#<config>]`.
454///
455/// `<locator>` is a valid [`Locator`] and `<config>` is of the form
456/// `<key1>=<value1>;...;<keyN>=<valueN>` where keys are alphabetically sorted. `<config>` is
457/// optional and can be provided to configure some aspects for an [`EndPoint`], e.g. the interface
458/// to listen on or connect to.
459///
460/// A full [`EndPoint`] string is hence in the form of `<proto>/<address>[?<metadata>][#config]`.
461///
462/// ## Metadata
463///
464/// - **`prio`**: a priority range bounded inclusively below and above (e.g. `2-4` signifies
465///   priorities 2, 3 and 4). This value is used to select the link used for transmission based on
466///   the Priority of the message in question.
467///
468///   For example, `tcp/localhost:7447?prio=1-3` assigns priorities
469///   [`Priority::RealTime`](crate::core::Priority::RealTime), [Priority::InteractiveHigh](crate::core::Priority::InteractiveHigh) and
470///   [`Priority::InteractiveLow`](crate::core::Priority::InteractiveLow) to the established link.
471///
472/// - **`rel`**: either "0" for [`Reliability::BestEffort`](crate::core::Reliability::BestEffort) or "1" for
473///   [`Reliability::Reliable`](crate::core::Reliability::Reliable). This value is used to select the link used for
474///   transmission based on the reliability of the message in question.
475///
476///   For example, `tcp/localhost:7447?prio=6-7;rel=0` assigns priorities
477///   [`Priority::DataLow`](crate::core::Priority::DataLow) and [Priority::Background](crate::core::Priority::Background), and
478///   [`Reliability::BestEffort`](crate::core::Reliability::BestEffort) to the established link.
479#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
480#[serde(into = "String")]
481#[serde(try_from = "String")]
482pub struct EndPoint {
483    pub(super) inner: String,
484}
485
486impl EndPoint {
487    pub fn new<A, B, C, D>(protocol: A, address: B, metadata: C, config: D) -> ZResult<Self>
488    where
489        A: AsRef<str>,
490        B: AsRef<str>,
491        C: AsRef<str>,
492        D: AsRef<str>,
493    {
494        let p: &str = protocol.as_ref();
495        let a: &str = address.as_ref();
496        let m: &str = metadata.as_ref();
497        let c: &str = config.as_ref();
498
499        let len = p.len() + a.len() + m.len();
500        if len > u8::MAX as usize {
501            bail!("Endpoint too big: {} bytes. Max: {} bytes. ", len, u8::MAX);
502        }
503
504        let s = match (m.is_empty(), c.is_empty()) {
505            (true, true) => format!("{p}{PROTO_SEPARATOR}{a}"),
506            (false, true) => format!("{p}{PROTO_SEPARATOR}{a}{METADATA_SEPARATOR}{m}"),
507            (true, false) => format!("{p}{PROTO_SEPARATOR}{a}{CONFIG_SEPARATOR}{c}"),
508            (false, false) => {
509                format!("{p}{PROTO_SEPARATOR}{a}{METADATA_SEPARATOR}{m}{CONFIG_SEPARATOR}{c}")
510            }
511        };
512
513        Self::try_from(s)
514    }
515
516    pub fn as_str(&self) -> &str {
517        self.inner.as_str()
518    }
519
520    pub fn split(&self) -> (Protocol<'_>, Address<'_>, Metadata<'_>, Config<'_>) {
521        (
522            self.protocol(),
523            self.address(),
524            self.metadata(),
525            self.config(),
526        )
527    }
528
529    pub fn protocol(&self) -> Protocol<'_> {
530        Protocol(protocol(self.inner.as_str()))
531    }
532
533    pub fn protocol_mut(&mut self) -> ProtocolMut<'_> {
534        ProtocolMut(self)
535    }
536
537    pub fn address(&self) -> Address<'_> {
538        Address(address(self.inner.as_str()))
539    }
540
541    pub fn address_mut(&mut self) -> AddressMut<'_> {
542        AddressMut(self)
543    }
544
545    pub fn metadata(&self) -> Metadata<'_> {
546        Metadata(metadata(self.inner.as_str()))
547    }
548
549    pub fn metadata_mut(&mut self) -> MetadataMut<'_> {
550        MetadataMut(self)
551    }
552
553    pub fn config(&self) -> Config<'_> {
554        Config(config(self.inner.as_str()))
555    }
556
557    pub fn config_mut(&mut self) -> ConfigMut<'_> {
558        ConfigMut(self)
559    }
560
561    pub fn to_locator(&self) -> Locator {
562        self.clone().into()
563    }
564}
565
566impl From<Locator> for EndPoint {
567    fn from(val: Locator) -> Self {
568        val.0
569    }
570}
571
572impl fmt::Display for EndPoint {
573    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574        f.write_str(&self.inner)
575    }
576}
577
578impl fmt::Debug for EndPoint {
579    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
580        write!(f, "{self}")
581    }
582}
583
584impl From<EndPoint> for String {
585    fn from(v: EndPoint) -> String {
586        v.inner
587    }
588}
589
590impl TryFrom<String> for EndPoint {
591    type Error = ZError;
592
593    fn try_from(s: String) -> Result<Self, Self::Error> {
594        const ERR: &str =
595            "Endpoints must be of the form <protocol>/<address>[?<metadata>][#<config>]";
596
597        let pidx = s
598            .find(PROTO_SEPARATOR)
599            .and_then(|i| (!s[..i].is_empty() && !s[i + 1..].is_empty()).then_some(i))
600            .ok_or_else(|| zerror!("{}: {}", ERR, s))?;
601
602        match (s.find(METADATA_SEPARATOR), s.find(CONFIG_SEPARATOR)) {
603            // No metadata or config at all
604            (None, None) => Ok(EndPoint { inner: s }),
605            // There is some metadata
606            (Some(midx), None) if midx > pidx && !s[midx + 1..].is_empty() => {
607                let mut inner = String::with_capacity(s.len());
608                inner.push_str(&s[..midx + 1]); // Includes metadata separator
609                parameters::from_iter_into(
610                    parameters::sort(parameters::iter(&s[midx + 1..])),
611                    &mut inner,
612                );
613                Ok(EndPoint { inner })
614            }
615            // There is some config
616            (None, Some(cidx)) if cidx > pidx && !s[cidx + 1..].is_empty() => {
617                let mut inner = String::with_capacity(s.len());
618                inner.push_str(&s[..cidx + 1]); // Includes config separator
619                parameters::from_iter_into(
620                    parameters::sort(parameters::iter(&s[cidx + 1..])),
621                    &mut inner,
622                );
623                Ok(EndPoint { inner })
624            }
625            // There is some metadata and some config
626            (Some(midx), Some(cidx))
627                if midx > pidx
628                    && cidx > midx
629                    && !s[midx + 1..cidx].is_empty()
630                    && !s[cidx + 1..].is_empty() =>
631            {
632                let mut inner = String::with_capacity(s.len());
633                inner.push_str(&s[..midx + 1]); // Includes metadata separator
634
635                parameters::from_iter_into(
636                    parameters::sort(parameters::iter(&s[midx + 1..cidx])),
637                    &mut inner,
638                );
639
640                inner.push(CONFIG_SEPARATOR);
641                parameters::from_iter_into(
642                    parameters::sort(parameters::iter(&s[cidx + 1..])),
643                    &mut inner,
644                );
645
646                Ok(EndPoint { inner })
647            }
648            _ => Err(zerror!("{}: {}", ERR, s).into()),
649        }
650    }
651}
652
653impl FromStr for EndPoint {
654    type Err = ZError;
655
656    fn from_str(s: &str) -> Result<Self, Self::Err> {
657        Self::try_from(s.to_owned())
658    }
659}
660
661impl EndPoint {
662    #[cfg(feature = "test")]
663    #[doc(hidden)]
664    pub fn rand() -> Self {
665        use rand::{
666            distributions::{Alphanumeric, DistString},
667            Rng,
668        };
669
670        const MIN: usize = 2;
671        const MAX: usize = 8;
672
673        let mut rng = rand::thread_rng();
674        let mut endpoint = String::new();
675
676        let len = rng.gen_range(MIN..MAX);
677        let proto = Alphanumeric.sample_string(&mut rng, len);
678        endpoint.push_str(proto.as_str());
679
680        endpoint.push(PROTO_SEPARATOR);
681
682        let len = rng.gen_range(MIN..MAX);
683        let address = Alphanumeric.sample_string(&mut rng, len);
684        endpoint.push_str(address.as_str());
685
686        if rng.gen_bool(0.5) {
687            endpoint.push(METADATA_SEPARATOR);
688            parameters::rand(&mut endpoint);
689        }
690        if rng.gen_bool(0.5) {
691            endpoint.push(CONFIG_SEPARATOR);
692            parameters::rand(&mut endpoint);
693        }
694
695        endpoint.parse().unwrap()
696    }
697}
698
699#[test]
700fn endpoints() {
701    assert!(EndPoint::from_str("/").is_err());
702    assert!(EndPoint::from_str("?").is_err());
703    assert!(EndPoint::from_str("#").is_err());
704
705    assert!(EndPoint::from_str("udp").is_err());
706    assert!(EndPoint::from_str("/udp").is_err());
707    assert!(EndPoint::from_str("udp/").is_err());
708
709    assert!(EndPoint::from_str("udp/127.0.0.1:7447?").is_err());
710    assert!(EndPoint::from_str("udp?127.0.0.1:7447").is_err());
711    assert!(EndPoint::from_str("udp?127.0.0.1:7447/meta").is_err());
712
713    assert!(EndPoint::from_str("udp/127.0.0.1:7447#").is_err());
714    assert!(EndPoint::from_str("udp/127.0.0.1:7447?#").is_err());
715    assert!(EndPoint::from_str("udp/127.0.0.1:7447#?").is_err());
716    assert!(EndPoint::from_str("udp#127.0.0.1:7447/").is_err());
717    assert!(EndPoint::from_str("udp#127.0.0.1:7447/?").is_err());
718    assert!(EndPoint::from_str("udp/127.0.0.1:7447?a=1#").is_err());
719
720    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447").unwrap();
721    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447");
722    assert_eq!(endpoint.protocol().as_str(), "udp");
723    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
724    assert!(endpoint.metadata().as_str().is_empty());
725    assert_eq!(endpoint.metadata().iter().count(), 0);
726
727    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2").unwrap();
728    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2");
729    assert_eq!(endpoint.protocol().as_str(), "udp");
730    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
731    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
732    assert_eq!(endpoint.metadata().iter().count(), 2);
733    endpoint
734        .metadata()
735        .iter()
736        .find(|x| x == &("a", "1"))
737        .unwrap();
738    assert_eq!(endpoint.metadata().get("a"), Some("1"));
739    endpoint
740        .metadata()
741        .iter()
742        .find(|x| x == &("b", "2"))
743        .unwrap();
744    assert_eq!(endpoint.metadata().get("b"), Some("2"));
745    assert!(endpoint.config().as_str().is_empty());
746    assert_eq!(endpoint.config().iter().count(), 0);
747
748    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;a=1").unwrap();
749    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2");
750    assert_eq!(endpoint.protocol().as_str(), "udp");
751    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
752    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
753    assert_eq!(endpoint.metadata().iter().count(), 2);
754    endpoint
755        .metadata()
756        .iter()
757        .find(|x| x == &("a", "1"))
758        .unwrap();
759    assert_eq!(endpoint.metadata().get("a"), Some("1"));
760    endpoint
761        .metadata()
762        .iter()
763        .find(|x| x == &("b", "2"))
764        .unwrap();
765    assert_eq!(endpoint.metadata().get("a"), Some("1"));
766    assert!(endpoint.config().as_str().is_empty());
767    assert_eq!(endpoint.config().iter().count(), 0);
768
769    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447#A=1;B=2").unwrap();
770    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447#A=1;B=2");
771    assert_eq!(endpoint.protocol().as_str(), "udp");
772    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
773    assert!(endpoint.metadata().as_str().is_empty());
774    assert_eq!(endpoint.metadata().iter().count(), 0);
775    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
776    assert_eq!(endpoint.config().iter().count(), 2);
777    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
778    assert_eq!(endpoint.config().get("A"), Some("1"));
779    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
780    assert_eq!(endpoint.config().get("B"), Some("2"));
781
782    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447#B=2;A=1").unwrap();
783    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447#A=1;B=2");
784    assert_eq!(endpoint.protocol().as_str(), "udp");
785    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
786    assert!(endpoint.metadata().as_str().is_empty());
787    assert_eq!(endpoint.metadata().iter().count(), 0);
788    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
789    assert_eq!(endpoint.config().iter().count(), 2);
790    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
791    assert_eq!(endpoint.config().get("A"), Some("1"));
792    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
793    assert_eq!(endpoint.config().get("B"), Some("2"));
794
795    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2#A=1;B=2").unwrap();
796    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2#A=1;B=2");
797    assert_eq!(endpoint.protocol().as_str(), "udp");
798    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
799    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
800    assert_eq!(endpoint.metadata().iter().count(), 2);
801    endpoint
802        .metadata()
803        .iter()
804        .find(|x| x == &("a", "1"))
805        .unwrap();
806    assert_eq!(endpoint.metadata().get("a"), Some("1"));
807    endpoint
808        .metadata()
809        .iter()
810        .find(|x| x == &("b", "2"))
811        .unwrap();
812    assert_eq!(endpoint.metadata().get("b"), Some("2"));
813    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
814    assert_eq!(endpoint.config().iter().count(), 2);
815    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
816    assert_eq!(endpoint.config().get("A"), Some("1"));
817    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
818    assert_eq!(endpoint.config().get("B"), Some("2"));
819
820    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;a=1#B=2;A=1").unwrap();
821    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2#A=1;B=2");
822    assert_eq!(endpoint.protocol().as_str(), "udp");
823    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
824    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
825    assert_eq!(endpoint.metadata().iter().count(), 2);
826    endpoint
827        .metadata()
828        .iter()
829        .find(|x| x == &("a", "1"))
830        .unwrap();
831    assert_eq!(endpoint.metadata().get("a"), Some("1"));
832    endpoint
833        .metadata()
834        .iter()
835        .find(|x| x == &("b", "2"))
836        .unwrap();
837    assert_eq!(endpoint.metadata().get("b"), Some("2"));
838    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
839    assert_eq!(endpoint.config().iter().count(), 2);
840    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
841    assert_eq!(endpoint.config().get("A"), Some("1"));
842    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
843    assert_eq!(endpoint.config().get("B"), Some("2"));
844
845    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2").unwrap();
846    endpoint.metadata_mut().insert("c", "3").unwrap();
847    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2;c=3");
848
849    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;c=3").unwrap();
850    endpoint.metadata_mut().insert("a", "1").unwrap();
851    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2;c=3");
852
853    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2").unwrap();
854    endpoint.config_mut().insert("A", "1").unwrap();
855    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2#A=1");
856
857    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;c=3#B=2").unwrap();
858    endpoint.config_mut().insert("A", "1").unwrap();
859    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?b=2;c=3#A=1;B=2");
860
861    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447").unwrap();
862    endpoint
863        .metadata_mut()
864        .extend_from_iter([("a", "1"), ("c", "3"), ("b", "2")].iter().copied())
865        .unwrap();
866    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2;c=3");
867
868    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447").unwrap();
869    endpoint
870        .config_mut()
871        .extend_from_iter([("A", "1"), ("C", "3"), ("B", "2")].iter().copied())
872        .unwrap();
873    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447#A=1;B=2;C=3");
874
875    let endpoint =
876        EndPoint::from_str("udp/127.0.0.1:7447#iface=en0;join=224.0.0.1|224.0.0.2|224.0.0.3")
877            .unwrap();
878    let c = endpoint.config();
879    assert_eq!(c.get("iface"), Some("en0"));
880    assert_eq!(c.get("join"), Some("224.0.0.1|224.0.0.2|224.0.0.3"));
881    assert_eq!(c.values("iface").count(), 1);
882    let mut i = c.values("iface");
883    assert_eq!(i.next(), Some("en0"));
884    assert_eq!(c.values("join").count(), 3);
885    let mut i = c.values("join");
886    assert_eq!(i.next(), Some("224.0.0.1"));
887    assert_eq!(i.next(), Some("224.0.0.2"));
888    assert_eq!(i.next(), Some("224.0.0.3"));
889    assert_eq!(i.next(), None);
890}