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)]
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
76impl TryFrom<u8> for WhatAmI {
77    type Error = ();
78
79    fn try_from(v: u8) -> Result<Self, Self::Error> {
80        match v {
81            Self::U8_R => Ok(Self::Router),
82            Self::U8_P => Ok(Self::Peer),
83            Self::U8_C => Ok(Self::Client),
84            _ => Err(()),
85        }
86    }
87}
88
89impl FromStr for WhatAmI {
90    type Err = ZError;
91
92    fn from_str(s: &str) -> Result<Self, Self::Err> {
93        match s {
94            Self::STR_R => Ok(Self::Router),
95            Self::STR_P => Ok(Self::Peer),
96            Self::STR_C => Ok(Self::Client),
97            _ => bail!(
98                "{s} is not a valid WhatAmI value. Valid values are: {}, {}, {}.",
99                Self::STR_R,
100                Self::STR_P,
101                Self::STR_C
102            ),
103        }
104    }
105}
106
107impl fmt::Display for WhatAmI {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        f.write_str(self.to_str())
110    }
111}
112
113impl From<WhatAmI> for u8 {
114    fn from(w: WhatAmI) -> Self {
115        w as u8
116    }
117}
118
119/// A helper type that allows matching combinations of `WhatAmI` values in scouting.
120///
121/// The [`scout`](crate::scouting::scout) function accepts a `WhatAmIMatcher` to filter the nodes
122/// of the specified types. The `WhatAmIMatcher` can be constructed from [`WhatAmI`] values
123/// with the `|` operator
124#[repr(transparent)]
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
126pub struct WhatAmIMatcher(NonZeroU8);
127
128impl WhatAmIMatcher {
129    // We use the 7th bit for detecting whether the WhatAmIMatcher is non-zero
130    const U8_0: u8 = 1 << 7;
131    const U8_R: u8 = Self::U8_0 | WhatAmI::U8_R;
132    const U8_P: u8 = Self::U8_0 | WhatAmI::U8_P;
133    const U8_C: u8 = Self::U8_0 | WhatAmI::U8_C;
134    const U8_R_P: u8 = Self::U8_0 | WhatAmI::U8_R | WhatAmI::U8_P;
135    const U8_P_C: u8 = Self::U8_0 | WhatAmI::U8_P | WhatAmI::U8_C;
136    const U8_R_C: u8 = Self::U8_0 | WhatAmI::U8_R | WhatAmI::U8_C;
137    const U8_R_P_C: u8 = Self::U8_0 | WhatAmI::U8_R | WhatAmI::U8_P | WhatAmI::U8_C;
138
139    /// Creates an empty `WhatAmIMatcher`, which matches no `WhatAmI` values.
140    pub const fn empty() -> Self {
141        Self(unsafe { NonZeroU8::new_unchecked(Self::U8_0) })
142    }
143
144    /// Creates a `WhatAmIMatcher` matching all [`WhatAmI::Router`] values.
145    pub const fn router(self) -> Self {
146        Self(unsafe { NonZeroU8::new_unchecked(self.0.get() | Self::U8_R) })
147    }
148
149    /// Creates a `WhatAmIMatcher` matching all [`WhatAmI::Peer`] values.
150    pub const fn peer(self) -> Self {
151        Self(unsafe { NonZeroU8::new_unchecked(self.0.get() | Self::U8_P) })
152    }
153
154    /// Creates a `WhatAmIMatcher` matching all [`WhatAmI::Client`] values.
155    pub const fn client(self) -> Self {
156        Self(unsafe { NonZeroU8::new_unchecked(self.0.get() | Self::U8_C) })
157    }
158
159    /// Returns whether the `WhatAmIMatcher` is empty.
160    pub const fn is_empty(&self) -> bool {
161        self.0.get() == Self::U8_0
162    }
163
164    /// Returns whether the `WhatAmIMatcher` matches the given `WhatAmI` value.
165    pub const fn matches(&self, w: WhatAmI) -> bool {
166        (self.0.get() & w as u8) != 0
167    }
168
169    /// Returns a string representation of the `WhatAmIMatcher` as a combination of
170    /// `WhatAmI` string representations separated by `|`.
171    pub const fn to_str(self) -> &'static str {
172        match self.0.get() {
173            Self::U8_0 => "",
174            Self::U8_R => WhatAmI::STR_R,
175            Self::U8_P => WhatAmI::STR_P,
176            Self::U8_C => WhatAmI::STR_C,
177            Self::U8_R_P => formatcp!("{}|{}", WhatAmI::STR_R, WhatAmI::STR_P),
178            Self::U8_R_C => formatcp!("{}|{}", WhatAmI::STR_R, WhatAmI::STR_C),
179            Self::U8_P_C => formatcp!("{}|{}", WhatAmI::STR_P, WhatAmI::STR_C),
180            Self::U8_R_P_C => formatcp!("{}|{}|{}", WhatAmI::STR_R, WhatAmI::STR_P, WhatAmI::STR_C),
181
182            _ => unreachable!(),
183        }
184    }
185
186    #[cfg(feature = "test")]
187    #[doc(hidden)]
188    pub fn rand() -> Self {
189        use rand::Rng;
190
191        let mut rng = rand::thread_rng();
192        let mut waim = WhatAmIMatcher::empty();
193        if rng.gen_bool(0.5) {
194            waim = waim.router();
195        }
196        if rng.gen_bool(0.5) {
197            waim = waim.peer();
198        }
199        if rng.gen_bool(0.5) {
200            waim = waim.client();
201        }
202        waim
203    }
204}
205
206impl TryFrom<u8> for WhatAmIMatcher {
207    type Error = ();
208
209    fn try_from(v: u8) -> Result<Self, Self::Error> {
210        const MIN: u8 = 0;
211        const MAX: u8 = WhatAmI::U8_R | WhatAmI::U8_P | WhatAmI::U8_C;
212
213        if (MIN..=MAX).contains(&v) {
214            Ok(WhatAmIMatcher(unsafe {
215                NonZeroU8::new_unchecked(Self::U8_0 | v)
216            }))
217        } else {
218            Err(())
219        }
220    }
221}
222
223impl FromStr for WhatAmIMatcher {
224    type Err = ();
225
226    fn from_str(s: &str) -> Result<Self, Self::Err> {
227        let mut inner = 0;
228        for s in s.split('|') {
229            match s.trim() {
230                "" => {}
231                WhatAmI::STR_R => inner |= WhatAmI::U8_R,
232                WhatAmI::STR_P => inner |= WhatAmI::U8_P,
233                WhatAmI::STR_C => inner |= WhatAmI::U8_C,
234                _ => return Err(()),
235            }
236        }
237        Self::try_from(inner)
238    }
239}
240
241impl fmt::Display for WhatAmIMatcher {
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        f.write_str(self.to_str())
244    }
245}
246
247impl From<WhatAmIMatcher> for u8 {
248    fn from(w: WhatAmIMatcher) -> u8 {
249        w.0.get()
250    }
251}
252
253impl<T> BitOr<T> for WhatAmIMatcher
254where
255    NonZeroU8: BitOr<T, Output = NonZeroU8>,
256{
257    type Output = Self;
258
259    fn bitor(self, rhs: T) -> Self::Output {
260        WhatAmIMatcher(self.0 | rhs)
261    }
262}
263
264impl BitOr<WhatAmI> for WhatAmIMatcher {
265    type Output = Self;
266
267    fn bitor(self, rhs: WhatAmI) -> Self::Output {
268        self | rhs as u8
269    }
270}
271
272impl BitOr for WhatAmIMatcher {
273    type Output = Self;
274
275    fn bitor(self, rhs: Self) -> Self::Output {
276        self | rhs.0
277    }
278}
279
280impl BitOr for WhatAmI {
281    type Output = WhatAmIMatcher;
282
283    fn bitor(self, rhs: Self) -> Self::Output {
284        WhatAmIMatcher(unsafe {
285            NonZeroU8::new_unchecked(self as u8 | rhs as u8 | WhatAmIMatcher::U8_0)
286        })
287    }
288}
289
290impl From<WhatAmI> for WhatAmIMatcher {
291    fn from(w: WhatAmI) -> Self {
292        WhatAmIMatcher(unsafe { NonZeroU8::new_unchecked(w as u8 | WhatAmIMatcher::U8_0) })
293    }
294}
295
296// Serde
297impl serde::Serialize for WhatAmI {
298    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
299    where
300        S: serde::Serializer,
301    {
302        serializer.serialize_str(self.to_str())
303    }
304}
305
306pub struct WhatAmIVisitor;
307
308impl<'de> serde::de::Visitor<'de> for WhatAmIVisitor {
309    type Value = WhatAmI;
310
311    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
312        write!(
313            formatter,
314            "either '{}', '{}' or '{}'",
315            WhatAmI::STR_R,
316            WhatAmI::STR_P,
317            WhatAmI::STR_C
318        )
319    }
320    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
321    where
322        E: serde::de::Error,
323    {
324        v.parse().map_err(|_| {
325            serde::de::Error::unknown_variant(v, &[WhatAmI::STR_R, WhatAmI::STR_P, WhatAmI::STR_C])
326        })
327    }
328    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
329    where
330        E: serde::de::Error,
331    {
332        self.visit_str(v)
333    }
334    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
335    where
336        E: serde::de::Error,
337    {
338        self.visit_str(&v)
339    }
340}
341
342impl<'de> serde::Deserialize<'de> for WhatAmI {
343    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
344    where
345        D: serde::Deserializer<'de>,
346    {
347        deserializer.deserialize_str(WhatAmIVisitor)
348    }
349}
350
351impl serde::Serialize for WhatAmIMatcher {
352    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
353    where
354        S: serde::Serializer,
355    {
356        let values = [WhatAmI::Router, WhatAmI::Peer, WhatAmI::Client]
357            .iter()
358            .filter(|v| self.matches(**v));
359        let mut seq = serializer.serialize_seq(Some(values.clone().count()))?;
360        for v in values {
361            seq.serialize_element(v)?;
362        }
363        seq.end()
364    }
365}
366
367pub struct WhatAmIMatcherVisitor;
368impl<'de> serde::de::Visitor<'de> for WhatAmIMatcherVisitor {
369    type Value = WhatAmIMatcher;
370    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
371        write!(
372            formatter,
373            "a list of whatami variants ('{}', '{}', '{}')",
374            WhatAmI::STR_R,
375            WhatAmI::STR_P,
376            WhatAmI::STR_C
377        )
378    }
379
380    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
381    where
382        A: serde::de::SeqAccess<'de>,
383    {
384        let mut inner = 0;
385
386        while let Some(s) = seq.next_element::<String>()? {
387            match s.as_str() {
388                WhatAmI::STR_R => inner |= WhatAmI::U8_R,
389                WhatAmI::STR_P => inner |= WhatAmI::U8_P,
390                WhatAmI::STR_C => inner |= WhatAmI::U8_C,
391                _ => {
392                    return Err(serde::de::Error::invalid_value(
393                        serde::de::Unexpected::Str(&s),
394                        &formatcp!(
395                            "one of ('{}', '{}', '{}')",
396                            WhatAmI::STR_R,
397                            WhatAmI::STR_P,
398                            WhatAmI::STR_C
399                        ),
400                    ))
401                }
402            }
403        }
404
405        Ok(WhatAmIMatcher::try_from(inner)
406            .expect("`WhatAmIMatcher` should be valid by construction"))
407    }
408}
409
410impl<'de> serde::Deserialize<'de> for WhatAmIMatcher {
411    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
412    where
413        D: serde::Deserializer<'de>,
414    {
415        deserializer.deserialize_seq(WhatAmIMatcherVisitor)
416    }
417}