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///   [`crate::core::Priority::RealTime`], [`crate::core::Priority::InteractiveHigh`] and
470///   [`crate::core::Priority::InteractiveLow`] to the established link.
471///
472/// - **`rel`**: either "0" for [`crate::core::Reliability::BestEffort`] or "1" for
473///   [`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///   [`crate::core::Priority::DataLow`] and [`crate::core::Priority::Background`], and
478///   [`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    pub fn rand() -> Self {
664        use rand::{
665            distributions::{Alphanumeric, DistString},
666            Rng,
667        };
668
669        const MIN: usize = 2;
670        const MAX: usize = 8;
671
672        let mut rng = rand::thread_rng();
673        let mut endpoint = String::new();
674
675        let len = rng.gen_range(MIN..MAX);
676        let proto = Alphanumeric.sample_string(&mut rng, len);
677        endpoint.push_str(proto.as_str());
678
679        endpoint.push(PROTO_SEPARATOR);
680
681        let len = rng.gen_range(MIN..MAX);
682        let address = Alphanumeric.sample_string(&mut rng, len);
683        endpoint.push_str(address.as_str());
684
685        if rng.gen_bool(0.5) {
686            endpoint.push(METADATA_SEPARATOR);
687            parameters::rand(&mut endpoint);
688        }
689        if rng.gen_bool(0.5) {
690            endpoint.push(CONFIG_SEPARATOR);
691            parameters::rand(&mut endpoint);
692        }
693
694        endpoint.parse().unwrap()
695    }
696}
697
698#[test]
699fn endpoints() {
700    assert!(EndPoint::from_str("/").is_err());
701    assert!(EndPoint::from_str("?").is_err());
702    assert!(EndPoint::from_str("#").is_err());
703
704    assert!(EndPoint::from_str("udp").is_err());
705    assert!(EndPoint::from_str("/udp").is_err());
706    assert!(EndPoint::from_str("udp/").is_err());
707
708    assert!(EndPoint::from_str("udp/127.0.0.1:7447?").is_err());
709    assert!(EndPoint::from_str("udp?127.0.0.1:7447").is_err());
710    assert!(EndPoint::from_str("udp?127.0.0.1:7447/meta").is_err());
711
712    assert!(EndPoint::from_str("udp/127.0.0.1:7447#").is_err());
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?a=1#").is_err());
718
719    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447").unwrap();
720    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447");
721    assert_eq!(endpoint.protocol().as_str(), "udp");
722    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
723    assert!(endpoint.metadata().as_str().is_empty());
724    assert_eq!(endpoint.metadata().iter().count(), 0);
725
726    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2").unwrap();
727    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2");
728    assert_eq!(endpoint.protocol().as_str(), "udp");
729    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
730    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
731    assert_eq!(endpoint.metadata().iter().count(), 2);
732    endpoint
733        .metadata()
734        .iter()
735        .find(|x| x == &("a", "1"))
736        .unwrap();
737    assert_eq!(endpoint.metadata().get("a"), Some("1"));
738    endpoint
739        .metadata()
740        .iter()
741        .find(|x| x == &("b", "2"))
742        .unwrap();
743    assert_eq!(endpoint.metadata().get("b"), Some("2"));
744    assert!(endpoint.config().as_str().is_empty());
745    assert_eq!(endpoint.config().iter().count(), 0);
746
747    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;a=1").unwrap();
748    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2");
749    assert_eq!(endpoint.protocol().as_str(), "udp");
750    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
751    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
752    assert_eq!(endpoint.metadata().iter().count(), 2);
753    endpoint
754        .metadata()
755        .iter()
756        .find(|x| x == &("a", "1"))
757        .unwrap();
758    assert_eq!(endpoint.metadata().get("a"), Some("1"));
759    endpoint
760        .metadata()
761        .iter()
762        .find(|x| x == &("b", "2"))
763        .unwrap();
764    assert_eq!(endpoint.metadata().get("a"), Some("1"));
765    assert!(endpoint.config().as_str().is_empty());
766    assert_eq!(endpoint.config().iter().count(), 0);
767
768    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447#A=1;B=2").unwrap();
769    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447#A=1;B=2");
770    assert_eq!(endpoint.protocol().as_str(), "udp");
771    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
772    assert!(endpoint.metadata().as_str().is_empty());
773    assert_eq!(endpoint.metadata().iter().count(), 0);
774    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
775    assert_eq!(endpoint.config().iter().count(), 2);
776    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
777    assert_eq!(endpoint.config().get("A"), Some("1"));
778    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
779    assert_eq!(endpoint.config().get("B"), Some("2"));
780
781    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447#B=2;A=1").unwrap();
782    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447#A=1;B=2");
783    assert_eq!(endpoint.protocol().as_str(), "udp");
784    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
785    assert!(endpoint.metadata().as_str().is_empty());
786    assert_eq!(endpoint.metadata().iter().count(), 0);
787    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
788    assert_eq!(endpoint.config().iter().count(), 2);
789    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
790    assert_eq!(endpoint.config().get("A"), Some("1"));
791    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
792    assert_eq!(endpoint.config().get("B"), Some("2"));
793
794    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2#A=1;B=2").unwrap();
795    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2#A=1;B=2");
796    assert_eq!(endpoint.protocol().as_str(), "udp");
797    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
798    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
799    assert_eq!(endpoint.metadata().iter().count(), 2);
800    endpoint
801        .metadata()
802        .iter()
803        .find(|x| x == &("a", "1"))
804        .unwrap();
805    assert_eq!(endpoint.metadata().get("a"), Some("1"));
806    endpoint
807        .metadata()
808        .iter()
809        .find(|x| x == &("b", "2"))
810        .unwrap();
811    assert_eq!(endpoint.metadata().get("b"), Some("2"));
812    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
813    assert_eq!(endpoint.config().iter().count(), 2);
814    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
815    assert_eq!(endpoint.config().get("A"), Some("1"));
816    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
817    assert_eq!(endpoint.config().get("B"), Some("2"));
818
819    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;a=1#B=2;A=1").unwrap();
820    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2#A=1;B=2");
821    assert_eq!(endpoint.protocol().as_str(), "udp");
822    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
823    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
824    assert_eq!(endpoint.metadata().iter().count(), 2);
825    endpoint
826        .metadata()
827        .iter()
828        .find(|x| x == &("a", "1"))
829        .unwrap();
830    assert_eq!(endpoint.metadata().get("a"), Some("1"));
831    endpoint
832        .metadata()
833        .iter()
834        .find(|x| x == &("b", "2"))
835        .unwrap();
836    assert_eq!(endpoint.metadata().get("b"), Some("2"));
837    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
838    assert_eq!(endpoint.config().iter().count(), 2);
839    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
840    assert_eq!(endpoint.config().get("A"), Some("1"));
841    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
842    assert_eq!(endpoint.config().get("B"), Some("2"));
843
844    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2").unwrap();
845    endpoint.metadata_mut().insert("c", "3").unwrap();
846    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2;c=3");
847
848    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;c=3").unwrap();
849    endpoint.metadata_mut().insert("a", "1").unwrap();
850    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2;c=3");
851
852    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2").unwrap();
853    endpoint.config_mut().insert("A", "1").unwrap();
854    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2#A=1");
855
856    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;c=3#B=2").unwrap();
857    endpoint.config_mut().insert("A", "1").unwrap();
858    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?b=2;c=3#A=1;B=2");
859
860    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447").unwrap();
861    endpoint
862        .metadata_mut()
863        .extend_from_iter([("a", "1"), ("c", "3"), ("b", "2")].iter().copied())
864        .unwrap();
865    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2;c=3");
866
867    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447").unwrap();
868    endpoint
869        .config_mut()
870        .extend_from_iter([("A", "1"), ("C", "3"), ("B", "2")].iter().copied())
871        .unwrap();
872    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447#A=1;B=2;C=3");
873
874    let endpoint =
875        EndPoint::from_str("udp/127.0.0.1:7447#iface=en0;join=224.0.0.1|224.0.0.2|224.0.0.3")
876            .unwrap();
877    let c = endpoint.config();
878    assert_eq!(c.get("iface"), Some("en0"));
879    assert_eq!(c.get("join"), Some("224.0.0.1|224.0.0.2|224.0.0.3"));
880    assert_eq!(c.values("iface").count(), 1);
881    let mut i = c.values("iface");
882    assert_eq!(i.next(), Some("en0"));
883    assert_eq!(c.values("join").count(), 3);
884    let mut i = c.values("join");
885    assert_eq!(i.next(), Some("224.0.0.1"));
886    assert_eq!(i.next(), Some("224.0.0.2"));
887    assert_eq!(i.next(), Some("224.0.0.3"));
888    assert_eq!(i.next(), None);
889}