Skip to main content

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    pub const MULTISTREAM: &'static str = "multistream";
199    pub const MIXED_RELIABILITY: &'static str = "mixed_rel";
200
201    pub fn as_str(&self) -> &'a str {
202        self.0
203    }
204
205    pub fn is_empty(&'a self) -> bool {
206        self.as_str().is_empty()
207    }
208
209    pub fn iter(&'a self) -> impl DoubleEndedIterator<Item = (&'a str, &'a str)> + Clone {
210        parameters::iter(self.0)
211    }
212
213    pub fn get(&'a self, k: &str) -> Option<&'a str> {
214        parameters::get(self.0, k)
215    }
216
217    pub fn values(&'a self, k: &str) -> impl DoubleEndedIterator<Item = &'a str> {
218        parameters::values(self.0, k)
219    }
220}
221
222impl AsRef<str> for Metadata<'_> {
223    fn as_ref(&self) -> &str {
224        self.as_str()
225    }
226}
227
228impl fmt::Display for Metadata<'_> {
229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230        f.write_str(self.as_str())
231    }
232}
233
234impl fmt::Debug for Metadata<'_> {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        write!(f, "{self}")
237    }
238}
239
240#[repr(transparent)]
241#[derive(PartialEq, Eq, Hash)]
242pub struct MetadataMut<'a>(&'a mut EndPoint);
243
244impl<'a> MetadataMut<'a> {
245    pub fn as_str(&'a self) -> &'a str {
246        metadata(self.0.as_str())
247    }
248
249    pub fn is_empty(&'a self) -> bool {
250        self.as_str().is_empty()
251    }
252}
253
254impl MetadataMut<'_> {
255    pub fn extend_from_iter<'s, I, K, V>(&mut self, iter: I) -> ZResult<()>
256    where
257        I: Iterator<Item = (&'s K, &'s V)> + Clone,
258        K: Borrow<str> + 's + ?Sized,
259        V: Borrow<str> + 's + ?Sized,
260    {
261        let ep = EndPoint::new(
262            self.0.protocol(),
263            self.0.address(),
264            parameters::from_iter(parameters::sort(parameters::join(
265                self.0.metadata().iter(),
266                iter.map(|(k, v)| (k.borrow(), v.borrow())),
267            ))),
268            self.0.config(),
269        )?;
270
271        self.0.inner = ep.inner;
272        Ok(())
273    }
274
275    pub fn insert<K, V>(&mut self, k: K, v: V) -> ZResult<()>
276    where
277        K: Borrow<str>,
278        V: Borrow<str>,
279    {
280        let ep = EndPoint::new(
281            self.0.protocol(),
282            self.0.address(),
283            parameters::insert_sort(self.0.metadata().as_str(), k.borrow(), v.borrow()).0,
284            self.0.config(),
285        )?;
286
287        self.0.inner = ep.inner;
288        Ok(())
289    }
290
291    pub fn remove<K>(&mut self, k: K) -> ZResult<()>
292    where
293        K: Borrow<str>,
294    {
295        let ep = EndPoint::new(
296            self.0.protocol(),
297            self.0.address(),
298            parameters::remove(self.0.metadata().as_str(), k.borrow()).0,
299            self.0.config(),
300        )?;
301
302        self.0.inner = ep.inner;
303        Ok(())
304    }
305}
306
307impl AsRef<str> for MetadataMut<'_> {
308    fn as_ref(&self) -> &str {
309        self.as_str()
310    }
311}
312
313impl fmt::Display for MetadataMut<'_> {
314    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315        f.write_str(self.as_str())
316    }
317}
318
319impl fmt::Debug for MetadataMut<'_> {
320    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321        write!(f, "{self}")
322    }
323}
324
325// Config
326#[repr(transparent)]
327#[derive(Copy, Clone, PartialEq, Eq, Hash)]
328pub struct Config<'a>(pub(super) &'a str);
329
330impl<'a> Config<'a> {
331    pub fn as_str(&self) -> &'a str {
332        self.0
333    }
334
335    pub fn is_empty(&self) -> bool {
336        self.as_str().is_empty()
337    }
338
339    pub fn iter(&self) -> impl DoubleEndedIterator<Item = (&'a str, &'a str)> + Clone {
340        parameters::iter(self.0)
341    }
342
343    pub fn get(&self, k: &str) -> Option<&'a str> {
344        parameters::get(self.0, k)
345    }
346
347    pub fn values(&self, k: &str) -> impl DoubleEndedIterator<Item = &'a str> {
348        parameters::values(self.0, k)
349    }
350}
351
352impl AsRef<str> for Config<'_> {
353    fn as_ref(&self) -> &str {
354        self.as_str()
355    }
356}
357
358impl fmt::Display for Config<'_> {
359    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360        f.write_str(self.as_str())
361    }
362}
363
364impl fmt::Debug for Config<'_> {
365    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366        write!(f, "{self}")
367    }
368}
369
370#[repr(transparent)]
371#[derive(PartialEq, Eq, Hash)]
372pub struct ConfigMut<'a>(&'a mut EndPoint);
373
374impl<'a> ConfigMut<'a> {
375    pub fn as_str(&'a self) -> &'a str {
376        config(self.0.as_str())
377    }
378
379    pub fn is_empty(&'a self) -> bool {
380        self.as_str().is_empty()
381    }
382}
383
384impl ConfigMut<'_> {
385    pub fn extend_from_iter<'s, I, K, V>(&mut self, iter: I) -> ZResult<()>
386    where
387        I: Iterator<Item = (&'s K, &'s V)> + Clone,
388        K: Borrow<str> + 's + ?Sized,
389        V: Borrow<str> + 's + ?Sized,
390    {
391        let ep = EndPoint::new(
392            self.0.protocol(),
393            self.0.address(),
394            self.0.metadata(),
395            parameters::from_iter(parameters::sort(parameters::join(
396                self.0.config().iter(),
397                iter.map(|(k, v)| (k.borrow(), v.borrow())),
398            ))),
399        )?;
400
401        self.0.inner = ep.inner;
402        Ok(())
403    }
404
405    pub fn insert<K, V>(&mut self, k: K, v: V) -> ZResult<()>
406    where
407        K: Borrow<str>,
408        V: Borrow<str>,
409    {
410        let ep = EndPoint::new(
411            self.0.protocol(),
412            self.0.address(),
413            self.0.metadata(),
414            parameters::insert_sort(self.0.config().as_str(), k.borrow(), v.borrow()).0,
415        )?;
416
417        self.0.inner = ep.inner;
418        Ok(())
419    }
420
421    pub fn remove<K>(&mut self, k: K) -> ZResult<()>
422    where
423        K: Borrow<str>,
424    {
425        let ep = EndPoint::new(
426            self.0.protocol(),
427            self.0.address(),
428            self.0.metadata(),
429            parameters::remove(self.0.config().as_str(), k.borrow()).0,
430        )?;
431
432        self.0.inner = ep.inner;
433        Ok(())
434    }
435}
436
437impl AsRef<str> for ConfigMut<'_> {
438    fn as_ref(&self) -> &str {
439        self.as_str()
440    }
441}
442
443impl fmt::Display for ConfigMut<'_> {
444    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445        f.write_str(self.as_str())
446    }
447}
448
449impl fmt::Debug for ConfigMut<'_> {
450    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451        write!(f, "{self}")
452    }
453}
454
455/// A string that respects the [`EndPoint`] canon form: `<locator>[#<config>]`.
456///
457/// `<locator>` is a valid [`Locator`] and `<config>` is of the form
458/// `<key1>=<value1>;...;<keyN>=<valueN>` where keys are alphabetically sorted. `<config>` is
459/// optional and can be provided to configure some aspects for an [`EndPoint`], e.g. the interface
460/// to listen on or connect to.
461///
462/// A full [`EndPoint`] string is hence in the form of `<proto>/<address>[?<metadata>][#config]`.
463///
464/// ## Metadata
465///
466/// - **`prio`**: a priority range bounded inclusively below and above (e.g. `2-4` signifies
467///   priorities 2, 3 and 4). This value is used to select the link used for transmission based on
468///   the Priority of the message in question.
469///
470///   For example, `tcp/localhost:7447?prio=1-3` assigns priorities
471///   [`Priority::RealTime`](crate::core::Priority::RealTime), [Priority::InteractiveHigh](crate::core::Priority::InteractiveHigh) and
472///   [`Priority::InteractiveLow`](crate::core::Priority::InteractiveLow) to the established link.
473///
474/// - **`rel`**: either "0" for [`Reliability::BestEffort`](crate::core::Reliability::BestEffort) or "1" for
475///   [`Reliability::Reliable`](crate::core::Reliability::Reliable). This value is used to select the link used for
476///   transmission based on the reliability of the message in question.
477///
478///   For example, `tcp/localhost:7447?prio=6-7;rel=0` assigns priorities
479///   [`Priority::DataLow`](crate::core::Priority::DataLow) and [Priority::Background](crate::core::Priority::Background), and
480///   [`Reliability::BestEffort`](crate::core::Reliability::BestEffort) to the established link.
481#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
482#[serde(into = "String")]
483#[serde(try_from = "String")]
484pub struct EndPoint {
485    pub(super) inner: String,
486}
487
488impl EndPoint {
489    pub fn new<A, B, C, D>(protocol: A, address: B, metadata: C, config: D) -> ZResult<Self>
490    where
491        A: AsRef<str>,
492        B: AsRef<str>,
493        C: AsRef<str>,
494        D: AsRef<str>,
495    {
496        let p: &str = protocol.as_ref();
497        let a: &str = address.as_ref();
498        let m: &str = metadata.as_ref();
499        let c: &str = config.as_ref();
500
501        let len = p.len() + a.len() + m.len();
502        if len > u8::MAX as usize {
503            bail!("Endpoint too big: {} bytes. Max: {} bytes. ", len, u8::MAX);
504        }
505
506        let s = match (m.is_empty(), c.is_empty()) {
507            (true, true) => format!("{p}{PROTO_SEPARATOR}{a}"),
508            (false, true) => format!("{p}{PROTO_SEPARATOR}{a}{METADATA_SEPARATOR}{m}"),
509            (true, false) => format!("{p}{PROTO_SEPARATOR}{a}{CONFIG_SEPARATOR}{c}"),
510            (false, false) => {
511                format!("{p}{PROTO_SEPARATOR}{a}{METADATA_SEPARATOR}{m}{CONFIG_SEPARATOR}{c}")
512            }
513        };
514
515        Self::try_from(s)
516    }
517
518    pub fn as_str(&self) -> &str {
519        self.inner.as_str()
520    }
521
522    pub fn split(&self) -> (Protocol<'_>, Address<'_>, Metadata<'_>, Config<'_>) {
523        (
524            self.protocol(),
525            self.address(),
526            self.metadata(),
527            self.config(),
528        )
529    }
530
531    pub fn protocol(&self) -> Protocol<'_> {
532        Protocol(protocol(self.inner.as_str()))
533    }
534
535    pub fn protocol_mut(&mut self) -> ProtocolMut<'_> {
536        ProtocolMut(self)
537    }
538
539    pub fn address(&self) -> Address<'_> {
540        Address(address(self.inner.as_str()))
541    }
542
543    pub fn address_mut(&mut self) -> AddressMut<'_> {
544        AddressMut(self)
545    }
546
547    pub fn metadata(&self) -> Metadata<'_> {
548        Metadata(metadata(self.inner.as_str()))
549    }
550
551    pub fn metadata_mut(&mut self) -> MetadataMut<'_> {
552        MetadataMut(self)
553    }
554
555    pub fn config(&self) -> Config<'_> {
556        Config(config(self.inner.as_str()))
557    }
558
559    pub fn config_mut(&mut self) -> ConfigMut<'_> {
560        ConfigMut(self)
561    }
562
563    pub fn to_locator(&self) -> Locator {
564        self.clone().into()
565    }
566
567    /// Constructs an uninitialized empty EndPoint.
568    #[zenoh_macros::internal]
569    pub fn empty() -> Self {
570        EndPoint {
571            inner: String::default(),
572        }
573    }
574}
575
576impl From<Locator> for EndPoint {
577    fn from(val: Locator) -> Self {
578        val.0
579    }
580}
581
582impl fmt::Display for EndPoint {
583    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
584        f.write_str(&self.inner)
585    }
586}
587
588impl fmt::Debug for EndPoint {
589    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
590        write!(f, "{self}")
591    }
592}
593
594impl From<EndPoint> for String {
595    fn from(v: EndPoint) -> String {
596        v.inner
597    }
598}
599
600impl TryFrom<String> for EndPoint {
601    type Error = ZError;
602
603    fn try_from(s: String) -> Result<Self, Self::Error> {
604        const ERR: &str =
605            "Endpoints must be of the form <protocol>/<address>[?<metadata>][#<config>]";
606
607        let pidx = s
608            .find(PROTO_SEPARATOR)
609            .and_then(|i| (!s[..i].is_empty() && !s[i + 1..].is_empty()).then_some(i))
610            .ok_or_else(|| zerror!("{}: {}", ERR, s))?;
611
612        match (s.find(METADATA_SEPARATOR), s.find(CONFIG_SEPARATOR)) {
613            // No metadata or config at all
614            (None, None) => Ok(EndPoint { inner: s }),
615            // There is some metadata
616            (Some(midx), None) if midx > pidx && !s[midx + 1..].is_empty() => {
617                let mut inner = String::with_capacity(s.len());
618                inner.push_str(&s[..midx + 1]); // Includes metadata separator
619                parameters::from_iter_into(
620                    parameters::sort(parameters::iter(&s[midx + 1..])),
621                    &mut inner,
622                );
623                Ok(EndPoint { inner })
624            }
625            // There is some config
626            (None, Some(cidx)) if cidx > pidx && !s[cidx + 1..].is_empty() => {
627                let mut inner = String::with_capacity(s.len());
628                inner.push_str(&s[..cidx + 1]); // Includes config separator
629                parameters::from_iter_into(
630                    parameters::sort(parameters::iter(&s[cidx + 1..])),
631                    &mut inner,
632                );
633                Ok(EndPoint { inner })
634            }
635            // There is some metadata and some config
636            (Some(midx), Some(cidx))
637                if midx > pidx
638                    && cidx > midx
639                    && !s[midx + 1..cidx].is_empty()
640                    && !s[cidx + 1..].is_empty() =>
641            {
642                let mut inner = String::with_capacity(s.len());
643                inner.push_str(&s[..midx + 1]); // Includes metadata separator
644
645                parameters::from_iter_into(
646                    parameters::sort(parameters::iter(&s[midx + 1..cidx])),
647                    &mut inner,
648                );
649
650                inner.push(CONFIG_SEPARATOR);
651                parameters::from_iter_into(
652                    parameters::sort(parameters::iter(&s[cidx + 1..])),
653                    &mut inner,
654                );
655
656                Ok(EndPoint { inner })
657            }
658            _ => Err(zerror!("{}: {}", ERR, s).into()),
659        }
660    }
661}
662
663impl FromStr for EndPoint {
664    type Err = ZError;
665
666    fn from_str(s: &str) -> Result<Self, Self::Err> {
667        Self::try_from(s.to_owned())
668    }
669}
670
671impl EndPoint {
672    #[cfg(feature = "test")]
673    #[doc(hidden)]
674    pub fn rand() -> Self {
675        use rand::{
676            distributions::{Alphanumeric, DistString},
677            Rng,
678        };
679
680        const MIN: usize = 2;
681        const MAX: usize = 8;
682
683        let mut rng = rand::thread_rng();
684        let mut endpoint = String::new();
685
686        let len = rng.gen_range(MIN..MAX);
687        let proto = Alphanumeric.sample_string(&mut rng, len);
688        endpoint.push_str(proto.as_str());
689
690        endpoint.push(PROTO_SEPARATOR);
691
692        let len = rng.gen_range(MIN..MAX);
693        let address = Alphanumeric.sample_string(&mut rng, len);
694        endpoint.push_str(address.as_str());
695
696        if rng.gen_bool(0.5) {
697            endpoint.push(METADATA_SEPARATOR);
698            parameters::rand(&mut endpoint);
699        }
700        if rng.gen_bool(0.5) {
701            endpoint.push(CONFIG_SEPARATOR);
702            parameters::rand(&mut endpoint);
703        }
704
705        endpoint.parse().unwrap()
706    }
707}
708
709#[test]
710fn endpoints() {
711    assert!(EndPoint::from_str("/").is_err());
712    assert!(EndPoint::from_str("?").is_err());
713    assert!(EndPoint::from_str("#").is_err());
714
715    assert!(EndPoint::from_str("udp").is_err());
716    assert!(EndPoint::from_str("/udp").is_err());
717    assert!(EndPoint::from_str("udp/").is_err());
718
719    assert!(EndPoint::from_str("udp/127.0.0.1:7447?").is_err());
720    assert!(EndPoint::from_str("udp?127.0.0.1:7447").is_err());
721    assert!(EndPoint::from_str("udp?127.0.0.1:7447/meta").is_err());
722
723    assert!(EndPoint::from_str("udp/127.0.0.1:7447#").is_err());
724    assert!(EndPoint::from_str("udp/127.0.0.1:7447?#").is_err());
725    assert!(EndPoint::from_str("udp/127.0.0.1:7447#?").is_err());
726    assert!(EndPoint::from_str("udp#127.0.0.1:7447/").is_err());
727    assert!(EndPoint::from_str("udp#127.0.0.1:7447/?").is_err());
728    assert!(EndPoint::from_str("udp/127.0.0.1:7447?a=1#").is_err());
729
730    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447").unwrap();
731    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447");
732    assert_eq!(endpoint.protocol().as_str(), "udp");
733    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
734    assert!(endpoint.metadata().as_str().is_empty());
735    assert_eq!(endpoint.metadata().iter().count(), 0);
736
737    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2").unwrap();
738    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2");
739    assert_eq!(endpoint.protocol().as_str(), "udp");
740    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
741    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
742    assert_eq!(endpoint.metadata().iter().count(), 2);
743    endpoint
744        .metadata()
745        .iter()
746        .find(|x| x == &("a", "1"))
747        .unwrap();
748    assert_eq!(endpoint.metadata().get("a"), Some("1"));
749    endpoint
750        .metadata()
751        .iter()
752        .find(|x| x == &("b", "2"))
753        .unwrap();
754    assert_eq!(endpoint.metadata().get("b"), Some("2"));
755    assert!(endpoint.config().as_str().is_empty());
756    assert_eq!(endpoint.config().iter().count(), 0);
757
758    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;a=1").unwrap();
759    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2");
760    assert_eq!(endpoint.protocol().as_str(), "udp");
761    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
762    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
763    assert_eq!(endpoint.metadata().iter().count(), 2);
764    endpoint
765        .metadata()
766        .iter()
767        .find(|x| x == &("a", "1"))
768        .unwrap();
769    assert_eq!(endpoint.metadata().get("a"), Some("1"));
770    endpoint
771        .metadata()
772        .iter()
773        .find(|x| x == &("b", "2"))
774        .unwrap();
775    assert_eq!(endpoint.metadata().get("a"), Some("1"));
776    assert!(endpoint.config().as_str().is_empty());
777    assert_eq!(endpoint.config().iter().count(), 0);
778
779    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447#A=1;B=2").unwrap();
780    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447#A=1;B=2");
781    assert_eq!(endpoint.protocol().as_str(), "udp");
782    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
783    assert!(endpoint.metadata().as_str().is_empty());
784    assert_eq!(endpoint.metadata().iter().count(), 0);
785    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
786    assert_eq!(endpoint.config().iter().count(), 2);
787    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
788    assert_eq!(endpoint.config().get("A"), Some("1"));
789    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
790    assert_eq!(endpoint.config().get("B"), Some("2"));
791
792    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447#B=2;A=1").unwrap();
793    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447#A=1;B=2");
794    assert_eq!(endpoint.protocol().as_str(), "udp");
795    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
796    assert!(endpoint.metadata().as_str().is_empty());
797    assert_eq!(endpoint.metadata().iter().count(), 0);
798    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
799    assert_eq!(endpoint.config().iter().count(), 2);
800    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
801    assert_eq!(endpoint.config().get("A"), Some("1"));
802    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
803    assert_eq!(endpoint.config().get("B"), Some("2"));
804
805    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2#A=1;B=2").unwrap();
806    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2#A=1;B=2");
807    assert_eq!(endpoint.protocol().as_str(), "udp");
808    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
809    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
810    assert_eq!(endpoint.metadata().iter().count(), 2);
811    endpoint
812        .metadata()
813        .iter()
814        .find(|x| x == &("a", "1"))
815        .unwrap();
816    assert_eq!(endpoint.metadata().get("a"), Some("1"));
817    endpoint
818        .metadata()
819        .iter()
820        .find(|x| x == &("b", "2"))
821        .unwrap();
822    assert_eq!(endpoint.metadata().get("b"), Some("2"));
823    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
824    assert_eq!(endpoint.config().iter().count(), 2);
825    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
826    assert_eq!(endpoint.config().get("A"), Some("1"));
827    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
828    assert_eq!(endpoint.config().get("B"), Some("2"));
829
830    let endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;a=1#B=2;A=1").unwrap();
831    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2#A=1;B=2");
832    assert_eq!(endpoint.protocol().as_str(), "udp");
833    assert_eq!(endpoint.address().as_str(), "127.0.0.1:7447");
834    assert_eq!(endpoint.metadata().as_str(), "a=1;b=2");
835    assert_eq!(endpoint.metadata().iter().count(), 2);
836    endpoint
837        .metadata()
838        .iter()
839        .find(|x| x == &("a", "1"))
840        .unwrap();
841    assert_eq!(endpoint.metadata().get("a"), Some("1"));
842    endpoint
843        .metadata()
844        .iter()
845        .find(|x| x == &("b", "2"))
846        .unwrap();
847    assert_eq!(endpoint.metadata().get("b"), Some("2"));
848    assert_eq!(endpoint.config().as_str(), "A=1;B=2");
849    assert_eq!(endpoint.config().iter().count(), 2);
850    endpoint.config().iter().find(|x| x == &("A", "1")).unwrap();
851    assert_eq!(endpoint.config().get("A"), Some("1"));
852    endpoint.config().iter().find(|x| x == &("B", "2")).unwrap();
853    assert_eq!(endpoint.config().get("B"), Some("2"));
854
855    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2").unwrap();
856    endpoint.metadata_mut().insert("c", "3").unwrap();
857    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2;c=3");
858
859    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;c=3").unwrap();
860    endpoint.metadata_mut().insert("a", "1").unwrap();
861    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2;c=3");
862
863    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?a=1;b=2").unwrap();
864    endpoint.config_mut().insert("A", "1").unwrap();
865    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2#A=1");
866
867    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447?b=2;c=3#B=2").unwrap();
868    endpoint.config_mut().insert("A", "1").unwrap();
869    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?b=2;c=3#A=1;B=2");
870
871    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447").unwrap();
872    endpoint
873        .metadata_mut()
874        .extend_from_iter([("a", "1"), ("c", "3"), ("b", "2")].iter().copied())
875        .unwrap();
876    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447?a=1;b=2;c=3");
877
878    let mut endpoint = EndPoint::from_str("udp/127.0.0.1:7447").unwrap();
879    endpoint
880        .config_mut()
881        .extend_from_iter([("A", "1"), ("C", "3"), ("B", "2")].iter().copied())
882        .unwrap();
883    assert_eq!(endpoint.as_str(), "udp/127.0.0.1:7447#A=1;B=2;C=3");
884
885    let endpoint =
886        EndPoint::from_str("udp/127.0.0.1:7447#iface=en0;join=224.0.0.1|224.0.0.2|224.0.0.3")
887            .unwrap();
888    let c = endpoint.config();
889    assert_eq!(c.get("iface"), Some("en0"));
890    assert_eq!(c.get("join"), Some("224.0.0.1|224.0.0.2|224.0.0.3"));
891    assert_eq!(c.values("iface").count(), 1);
892    let mut i = c.values("iface");
893    assert_eq!(i.next(), Some("en0"));
894    assert_eq!(c.values("join").count(), 3);
895    let mut i = c.values("join");
896    assert_eq!(i.next(), Some("224.0.0.1"));
897    assert_eq!(i.next(), Some("224.0.0.2"));
898    assert_eq!(i.next(), Some("224.0.0.3"));
899    assert_eq!(i.next(), None);
900}