Skip to main content

zenoh_protocol/core/
whatami.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::string::String;
15use core::{convert::TryFrom, fmt, num::NonZeroU8, ops::BitOr, str::FromStr};
16
17use const_format::formatcp;
18use serde::ser::SerializeSeq;
19use zenoh_result::{bail, ZError};
20
21/// The type of the node in the Zenoh network.
22///
23/// The zenoh application can work in three different modes: router, peer, and client.
24///
25/// In the peer mode the application searches for other nodes and establishes direct connections
26/// with them. This can work using multicast discovery and by getting gossip information
27/// from the initial entry points. The peer mode is the default mode.
28///
29/// In the client mode the application remains connected to a single connection point, which
30/// serves as a gateway to the rest of the network. This mode is useful for constrained
31/// devices that cannot afford to maintain multiple connections.
32///
33/// The router mode is used to run a zenoh router, which is a node that
34/// maintains a predefined zenoh network topology. Unlike peers, routers do not
35/// discover other nodes by themselves, but rely on static configuration.
36///
37/// A more detailed explanation of each mode is at [Zenoh Documentation](https://zenoh.io/docs/getting-started/deployment/)
38#[repr(u8)]
39#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
40pub enum WhatAmI {
41    Router = 0b001,
42    #[default]
43    Peer = 0b010,
44    Client = 0b100,
45}
46
47impl WhatAmI {
48    const STR_R: &'static str = "router";
49    const STR_P: &'static str = "peer";
50    const STR_C: &'static str = "client";
51
52    const U8_R: u8 = Self::Router as u8;
53    const U8_P: u8 = Self::Peer as u8;
54    const U8_C: u8 = Self::Client as u8;
55
56    pub const fn to_str(self) -> &'static str {
57        match self {
58            Self::Router => Self::STR_R,
59            Self::Peer => Self::STR_P,
60            Self::Client => Self::STR_C,
61        }
62    }
63
64    #[cfg(feature = "test")]
65    #[doc(hidden)]
66    pub fn rand() -> Self {
67        use rand::prelude::SliceRandom;
68        let mut rng = rand::thread_rng();
69
70        *[Self::Router, Self::Peer, Self::Client]
71            .choose(&mut rng)
72            .unwrap()
73    }
74
75    pub const fn is_client(self) -> bool {
76        matches!(self, WhatAmI::Client)
77    }
78
79    pub const fn is_peer(self) -> bool {
80        matches!(self, WhatAmI::Peer)
81    }
82
83    pub const fn is_router(self) -> bool {
84        matches!(self, WhatAmI::Router)
85    }
86}
87
88impl TryFrom<u8> for WhatAmI {
89    type Error = ();
90
91    fn try_from(v: u8) -> Result<Self, Self::Error> {
92        match v {
93            Self::U8_R => Ok(Self::Router),
94            Self::U8_P => Ok(Self::Peer),
95            Self::U8_C => Ok(Self::Client),
96            _ => Err(()),
97        }
98    }
99}
100
101impl FromStr for WhatAmI {
102    type Err = ZError;
103
104    fn from_str(s: &str) -> Result<Self, Self::Err> {
105        match s {
106            Self::STR_R => Ok(Self::Router),
107            Self::STR_P => Ok(Self::Peer),
108            Self::STR_C => Ok(Self::Client),
109            _ => bail!(
110                "{s} is not a valid WhatAmI value. Valid values are: {}, {}, {}.",
111                Self::STR_R,
112                Self::STR_P,
113                Self::STR_C
114            ),
115        }
116    }
117}
118
119impl fmt::Display for WhatAmI {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        f.write_str(self.to_str())
122    }
123}
124
125impl From<WhatAmI> for u8 {
126    fn from(w: WhatAmI) -> Self {
127        w as u8
128    }
129}
130
131/// A helper type that allows matching combinations of `WhatAmI` values in scouting.
132///
133/// The [`scout`](crate::scouting::scout) function accepts a `WhatAmIMatcher` to filter the nodes
134/// of the specified types. The `WhatAmIMatcher` can be constructed from [`WhatAmI`] values
135/// with the `|` operator
136#[repr(transparent)]
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
138pub struct WhatAmIMatcher(NonZeroU8);
139
140impl WhatAmIMatcher {
141    // We use the 7th bit for detecting whether the WhatAmIMatcher is non-zero
142    const U8_0: u8 = 1 << 7;
143    const U8_R: u8 = Self::U8_0 | WhatAmI::U8_R;
144    const U8_P: u8 = Self::U8_0 | WhatAmI::U8_P;
145    const U8_C: u8 = Self::U8_0 | WhatAmI::U8_C;
146    const U8_R_P: u8 = Self::U8_0 | WhatAmI::U8_R | WhatAmI::U8_P;
147    const U8_P_C: u8 = Self::U8_0 | WhatAmI::U8_P | WhatAmI::U8_C;
148    const U8_R_C: u8 = Self::U8_0 | WhatAmI::U8_R | WhatAmI::U8_C;
149    const U8_R_P_C: u8 = Self::U8_0 | WhatAmI::U8_R | WhatAmI::U8_P | WhatAmI::U8_C;
150
151    /// Creates an empty `WhatAmIMatcher`, which matches no `WhatAmI` values.
152    pub const fn empty() -> Self {
153        Self(unsafe { NonZeroU8::new_unchecked(Self::U8_0) })
154    }
155
156    /// Creates a `WhatAmIMatcher` matching all [`WhatAmI::Router`] values.
157    pub const fn router(self) -> Self {
158        Self(unsafe { NonZeroU8::new_unchecked(self.0.get() | Self::U8_R) })
159    }
160
161    /// Creates a `WhatAmIMatcher` matching all [`WhatAmI::Peer`] values.
162    pub const fn peer(self) -> Self {
163        Self(unsafe { NonZeroU8::new_unchecked(self.0.get() | Self::U8_P) })
164    }
165
166    /// Creates a `WhatAmIMatcher` matching all [`WhatAmI::Client`] values.
167    pub const fn client(self) -> Self {
168        Self(unsafe { NonZeroU8::new_unchecked(self.0.get() | Self::U8_C) })
169    }
170
171    /// Returns whether the `WhatAmIMatcher` is empty.
172    pub const fn is_empty(&self) -> bool {
173        self.0.get() == Self::U8_0
174    }
175
176    /// Returns whether the `WhatAmIMatcher` matches the given `WhatAmI` value.
177    pub const fn matches(&self, w: WhatAmI) -> bool {
178        (self.0.get() & w as u8) != 0
179    }
180
181    /// Returns a string representation of the `WhatAmIMatcher` as a combination of
182    /// `WhatAmI` string representations separated by `|`.
183    pub const fn to_str(self) -> &'static str {
184        match self.0.get() {
185            Self::U8_0 => "",
186            Self::U8_R => WhatAmI::STR_R,
187            Self::U8_P => WhatAmI::STR_P,
188            Self::U8_C => WhatAmI::STR_C,
189            Self::U8_R_P => formatcp!("{}|{}", WhatAmI::STR_R, WhatAmI::STR_P),
190            Self::U8_R_C => formatcp!("{}|{}", WhatAmI::STR_R, WhatAmI::STR_C),
191            Self::U8_P_C => formatcp!("{}|{}", WhatAmI::STR_P, WhatAmI::STR_C),
192            Self::U8_R_P_C => formatcp!("{}|{}|{}", WhatAmI::STR_R, WhatAmI::STR_P, WhatAmI::STR_C),
193
194            _ => unreachable!(),
195        }
196    }
197
198    #[cfg(feature = "test")]
199    #[doc(hidden)]
200    pub fn rand() -> Self {
201        use rand::Rng;
202
203        let mut rng = rand::thread_rng();
204        let mut waim = WhatAmIMatcher::empty();
205        if rng.gen_bool(0.5) {
206            waim = waim.router();
207        }
208        if rng.gen_bool(0.5) {
209            waim = waim.peer();
210        }
211        if rng.gen_bool(0.5) {
212            waim = waim.client();
213        }
214        waim
215    }
216}
217
218impl TryFrom<u8> for WhatAmIMatcher {
219    type Error = ();
220
221    fn try_from(v: u8) -> Result<Self, Self::Error> {
222        const MIN: u8 = 0;
223        const MAX: u8 = WhatAmI::U8_R | WhatAmI::U8_P | WhatAmI::U8_C;
224
225        if (MIN..=MAX).contains(&v) {
226            Ok(WhatAmIMatcher(unsafe {
227                NonZeroU8::new_unchecked(Self::U8_0 | v)
228            }))
229        } else {
230            Err(())
231        }
232    }
233}
234
235impl FromStr for WhatAmIMatcher {
236    type Err = ();
237
238    fn from_str(s: &str) -> Result<Self, Self::Err> {
239        let mut inner = 0;
240        for s in s.split('|') {
241            match s.trim() {
242                "" => {}
243                WhatAmI::STR_R => inner |= WhatAmI::U8_R,
244                WhatAmI::STR_P => inner |= WhatAmI::U8_P,
245                WhatAmI::STR_C => inner |= WhatAmI::U8_C,
246                _ => return Err(()),
247            }
248        }
249        Self::try_from(inner)
250    }
251}
252
253impl fmt::Display for WhatAmIMatcher {
254    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255        f.write_str(self.to_str())
256    }
257}
258
259impl From<WhatAmIMatcher> for u8 {
260    fn from(w: WhatAmIMatcher) -> u8 {
261        w.0.get()
262    }
263}
264
265impl<T> BitOr<T> for WhatAmIMatcher
266where
267    NonZeroU8: BitOr<T, Output = NonZeroU8>,
268{
269    type Output = Self;
270
271    fn bitor(self, rhs: T) -> Self::Output {
272        WhatAmIMatcher(self.0 | rhs)
273    }
274}
275
276impl BitOr<WhatAmI> for WhatAmIMatcher {
277    type Output = Self;
278
279    fn bitor(self, rhs: WhatAmI) -> Self::Output {
280        self | rhs as u8
281    }
282}
283
284impl BitOr for WhatAmIMatcher {
285    type Output = Self;
286
287    fn bitor(self, rhs: Self) -> Self::Output {
288        self | rhs.0
289    }
290}
291
292impl BitOr for WhatAmI {
293    type Output = WhatAmIMatcher;
294
295    fn bitor(self, rhs: Self) -> Self::Output {
296        WhatAmIMatcher(unsafe {
297            NonZeroU8::new_unchecked(self as u8 | rhs as u8 | WhatAmIMatcher::U8_0)
298        })
299    }
300}
301
302impl From<WhatAmI> for WhatAmIMatcher {
303    fn from(w: WhatAmI) -> Self {
304        WhatAmIMatcher(unsafe { NonZeroU8::new_unchecked(w as u8 | WhatAmIMatcher::U8_0) })
305    }
306}
307
308// Serde
309impl serde::Serialize for WhatAmI {
310    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
311    where
312        S: serde::Serializer,
313    {
314        serializer.serialize_str(self.to_str())
315    }
316}
317
318#[derive(Debug)]
319pub struct WhatAmIVisitor;
320
321impl<'de> serde::de::Visitor<'de> for WhatAmIVisitor {
322    type Value = WhatAmI;
323
324    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
325        write!(
326            formatter,
327            "either '{}', '{}' or '{}'",
328            WhatAmI::STR_R,
329            WhatAmI::STR_P,
330            WhatAmI::STR_C
331        )
332    }
333    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
334    where
335        E: serde::de::Error,
336    {
337        v.parse().map_err(|_| {
338            serde::de::Error::unknown_variant(v, &[WhatAmI::STR_R, WhatAmI::STR_P, WhatAmI::STR_C])
339        })
340    }
341    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
342    where
343        E: serde::de::Error,
344    {
345        self.visit_str(v)
346    }
347    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
348    where
349        E: serde::de::Error,
350    {
351        self.visit_str(&v)
352    }
353}
354
355impl<'de> serde::Deserialize<'de> for WhatAmI {
356    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
357    where
358        D: serde::Deserializer<'de>,
359    {
360        deserializer.deserialize_str(WhatAmIVisitor)
361    }
362}
363
364impl serde::Serialize for WhatAmIMatcher {
365    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
366    where
367        S: serde::Serializer,
368    {
369        let values = [WhatAmI::Router, WhatAmI::Peer, WhatAmI::Client]
370            .iter()
371            .filter(|v| self.matches(**v));
372        let mut seq = serializer.serialize_seq(Some(values.clone().count()))?;
373        for v in values {
374            seq.serialize_element(v)?;
375        }
376        seq.end()
377    }
378}
379
380#[derive(Debug)]
381pub struct WhatAmIMatcherVisitor;
382impl<'de> serde::de::Visitor<'de> for WhatAmIMatcherVisitor {
383    type Value = WhatAmIMatcher;
384    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
385        write!(
386            formatter,
387            "a list of whatami variants ('{}', '{}', '{}')",
388            WhatAmI::STR_R,
389            WhatAmI::STR_P,
390            WhatAmI::STR_C
391        )
392    }
393
394    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
395    where
396        A: serde::de::SeqAccess<'de>,
397    {
398        let mut inner = 0;
399
400        while let Some(s) = seq.next_element::<String>()? {
401            match s.as_str() {
402                WhatAmI::STR_R => inner |= WhatAmI::U8_R,
403                WhatAmI::STR_P => inner |= WhatAmI::U8_P,
404                WhatAmI::STR_C => inner |= WhatAmI::U8_C,
405                _ => {
406                    return Err(serde::de::Error::invalid_value(
407                        serde::de::Unexpected::Str(&s),
408                        &formatcp!(
409                            "one of ('{}', '{}', '{}')",
410                            WhatAmI::STR_R,
411                            WhatAmI::STR_P,
412                            WhatAmI::STR_C
413                        ),
414                    ))
415                }
416            }
417        }
418
419        Ok(WhatAmIMatcher::try_from(inner)
420            .expect("`WhatAmIMatcher` should be valid by construction"))
421    }
422}
423
424impl<'de> serde::Deserialize<'de> for WhatAmIMatcher {
425    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
426    where
427        D: serde::Deserializer<'de>,
428    {
429        deserializer.deserialize_seq(WhatAmIMatcherVisitor)
430    }
431}