1use alloc::{
16 boxed::Box,
17 format,
18 string::{String, ToString},
19};
20use core::{
21 convert::{From, TryFrom, TryInto},
22 fmt::{self, Display},
23 hash::Hash,
24 ops::{Deref, RangeInclusive},
25 str::FromStr,
26};
27
28use serde::{Deserialize, Serialize};
29pub use uhlc::{Timestamp, NTP64};
30use zenoh_keyexpr::OwnedKeyExpr;
31use zenoh_result::{bail, zerror};
32
33pub type TimestampId = uhlc::ID;
35
36pub mod whatami;
38pub use whatami::*;
39pub use zenoh_keyexpr::key_expr;
40
41pub mod wire_expr;
42pub use wire_expr::*;
43
44mod cowstr;
45pub use cowstr::CowStr;
46pub mod encoding;
47pub use encoding::{Encoding, EncodingId};
48
49pub mod locator;
50pub use locator::*;
51
52pub mod endpoint;
53pub use endpoint::*;
54
55pub mod resolution;
56pub use resolution::*;
57
58pub mod parameters;
59pub use parameters::Parameters;
60
61#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
63#[repr(transparent)]
64pub struct ZenohIdProto(uhlc::ID);
65
66impl ZenohIdProto {
67 pub const MAX_SIZE: usize = 16;
68
69 #[inline]
70 pub fn size(&self) -> usize {
71 self.0.size()
72 }
73
74 #[inline]
75 pub fn to_le_bytes(&self) -> [u8; uhlc::ID::MAX_SIZE] {
76 self.0.to_le_bytes()
77 }
78
79 #[doc(hidden)]
80 pub fn rand() -> ZenohIdProto {
81 ZenohIdProto(uhlc::ID::rand())
82 }
83
84 pub fn into_keyexpr(self) -> OwnedKeyExpr {
85 self.into()
86 }
87}
88
89impl Default for ZenohIdProto {
90 fn default() -> Self {
91 Self::rand()
92 }
93}
94
95#[derive(Debug, Clone, Copy)]
97pub struct SizeError(usize);
98
99#[cfg(feature = "std")]
100impl std::error::Error for SizeError {}
101#[cfg(not(feature = "std"))]
102impl zenoh_result::IError for SizeError {}
103
104impl From<uhlc::SizeError> for SizeError {
105 fn from(val: uhlc::SizeError) -> Self {
106 Self(val.0)
107 }
108}
109
110impl fmt::Display for SizeError {
112 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113 write!(
114 f,
115 "Maximum ID size ({} bytes) exceeded: {}",
116 uhlc::ID::MAX_SIZE,
117 self.0
118 )
119 }
120}
121
122macro_rules! derive_tryfrom {
123 ($T: ty) => {
124 impl TryFrom<$T> for ZenohIdProto {
125 type Error = zenoh_result::Error;
126 fn try_from(val: $T) -> Result<Self, Self::Error> {
127 match val.try_into() {
128 Ok(ok) => Ok(Self(ok)),
129 Err(err) => Err(Box::<SizeError>::new(err.into())),
130 }
131 }
132 }
133 };
134}
135derive_tryfrom!([u8; 1]);
136derive_tryfrom!(&[u8; 1]);
137derive_tryfrom!([u8; 2]);
138derive_tryfrom!(&[u8; 2]);
139derive_tryfrom!([u8; 3]);
140derive_tryfrom!(&[u8; 3]);
141derive_tryfrom!([u8; 4]);
142derive_tryfrom!(&[u8; 4]);
143derive_tryfrom!([u8; 5]);
144derive_tryfrom!(&[u8; 5]);
145derive_tryfrom!([u8; 6]);
146derive_tryfrom!(&[u8; 6]);
147derive_tryfrom!([u8; 7]);
148derive_tryfrom!(&[u8; 7]);
149derive_tryfrom!([u8; 8]);
150derive_tryfrom!(&[u8; 8]);
151derive_tryfrom!([u8; 9]);
152derive_tryfrom!(&[u8; 9]);
153derive_tryfrom!([u8; 10]);
154derive_tryfrom!(&[u8; 10]);
155derive_tryfrom!([u8; 11]);
156derive_tryfrom!(&[u8; 11]);
157derive_tryfrom!([u8; 12]);
158derive_tryfrom!(&[u8; 12]);
159derive_tryfrom!([u8; 13]);
160derive_tryfrom!(&[u8; 13]);
161derive_tryfrom!([u8; 14]);
162derive_tryfrom!(&[u8; 14]);
163derive_tryfrom!([u8; 15]);
164derive_tryfrom!(&[u8; 15]);
165derive_tryfrom!([u8; 16]);
166derive_tryfrom!(&[u8; 16]);
167derive_tryfrom!(&[u8]);
168
169impl FromStr for ZenohIdProto {
170 type Err = zenoh_result::Error;
171
172 fn from_str(s: &str) -> Result<Self, Self::Err> {
173 if s.contains(|c: char| c.is_ascii_uppercase()) {
174 bail!(
175 "Invalid id: {} - uppercase hexadecimal is not accepted, use lowercase",
176 s
177 );
178 }
179 let u: uhlc::ID = s
180 .parse()
181 .map_err(|e: uhlc::ParseIDError| zerror!("Invalid id: {} - {}", s, e.cause))?;
182 Ok(ZenohIdProto(u))
183 }
184}
185
186impl fmt::Debug for ZenohIdProto {
187 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188 write!(f, "{}", self.0)
189 }
190}
191
192impl fmt::Display for ZenohIdProto {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 fmt::Debug::fmt(self, f)
195 }
196}
197
198impl From<&ZenohIdProto> for uhlc::ID {
200 fn from(zid: &ZenohIdProto) -> Self {
201 zid.0
202 }
203}
204
205impl From<ZenohIdProto> for uhlc::ID {
206 fn from(zid: ZenohIdProto) -> Self {
207 zid.0
208 }
209}
210
211impl From<ZenohIdProto> for OwnedKeyExpr {
212 fn from(zid: ZenohIdProto) -> Self {
213 unsafe { OwnedKeyExpr::from_string_unchecked(zid.to_string()) }
218 }
219}
220
221impl From<&ZenohIdProto> for OwnedKeyExpr {
222 fn from(zid: &ZenohIdProto) -> Self {
223 (*zid).into()
224 }
225}
226
227impl serde::Serialize for ZenohIdProto {
228 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
229 where
230 S: serde::Serializer,
231 {
232 serializer.serialize_str(self.to_string().as_str())
233 }
234}
235
236impl<'de> serde::Deserialize<'de> for ZenohIdProto {
237 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
238 where
239 D: serde::Deserializer<'de>,
240 {
241 struct ZenohIdVisitor;
242
243 impl<'de> serde::de::Visitor<'de> for ZenohIdVisitor {
244 type Value = ZenohIdProto;
245
246 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
247 formatter.write_str(&format!(
248 "An hex string of 1-{} bytes",
249 ZenohIdProto::MAX_SIZE
250 ))
251 }
252
253 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
254 where
255 E: serde::de::Error,
256 {
257 v.parse().map_err(serde::de::Error::custom)
258 }
259
260 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
261 where
262 E: serde::de::Error,
263 {
264 self.visit_str(v)
265 }
266
267 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
268 where
269 E: serde::de::Error,
270 {
271 self.visit_str(&v)
272 }
273 }
274
275 deserializer.deserialize_str(ZenohIdVisitor)
276 }
277}
278
279pub type EntityId = u32;
281
282#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)]
284pub struct EntityGlobalIdProto {
285 pub zid: ZenohIdProto,
286 pub eid: EntityId,
287}
288
289impl EntityGlobalIdProto {
290 #[cfg(feature = "test")]
291 #[doc(hidden)]
292 pub fn rand() -> Self {
293 use rand::Rng;
294 Self {
295 zid: ZenohIdProto::rand(),
296 eid: rand::thread_rng().gen(),
297 }
298 }
299}
300
301#[repr(u8)]
302#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)]
303pub enum Priority {
304 Control = 0,
305 RealTime = 1,
306 InteractiveHigh = 2,
307 InteractiveLow = 3,
308 DataHigh = 4,
309 #[default]
310 Data = 5,
311 DataLow = 6,
312 Background = 7,
313}
314
315impl Display for Priority {
316 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317 f.write_str(match self {
318 Priority::Control => "control",
319 Priority::RealTime => "real-time",
320 Priority::InteractiveHigh => "interactive-high",
321 Priority::InteractiveLow => "interactive-low",
322 Priority::Data => "data",
323 Priority::DataHigh => "data-high",
324 Priority::DataLow => "data-low",
325 Priority::Background => "background",
326 })
327 }
328}
329
330#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize)]
331pub struct PriorityRange(RangeInclusive<Priority>);
333
334impl Deref for PriorityRange {
335 type Target = RangeInclusive<Priority>;
336
337 fn deref(&self) -> &Self::Target {
338 &self.0
339 }
340}
341
342impl PriorityRange {
343 pub fn new(range: RangeInclusive<Priority>) -> Self {
344 Self(range)
345 }
346
347 pub fn includes(&self, other: &PriorityRange) -> bool {
349 self.start() <= other.start() && other.end() <= self.end()
350 }
351
352 pub fn len(&self) -> usize {
353 *self.end() as usize - *self.start() as usize + 1
354 }
355
356 pub fn is_empty(&self) -> bool {
357 false
358 }
359
360 #[cfg(feature = "test")]
361 #[doc(hidden)]
362 pub fn rand() -> Self {
363 use rand::Rng;
364 let mut rng = rand::thread_rng();
365 let start = rng.gen_range(Priority::MAX as u8..Priority::MIN as u8);
366 let end = rng.gen_range((start + 1)..=Priority::MIN as u8);
367
368 Self(Priority::try_from(start).unwrap()..=Priority::try_from(end).unwrap())
369 }
370}
371
372impl Display for PriorityRange {
373 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374 write!(f, "{}-{}", *self.start() as u8, *self.end() as u8)
375 }
376}
377
378#[derive(Debug, PartialEq)]
379pub enum InvalidPriorityRange {
380 InvalidSyntax { found: String },
381 InvalidBound { message: String },
382}
383
384impl Display for InvalidPriorityRange {
385 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
386 match self {
387 InvalidPriorityRange::InvalidSyntax { found } => write!(f, "invalid priority range string, expected an range of the form `start-end` but found {found}"),
388 InvalidPriorityRange::InvalidBound { message } => write!(f, "invalid priority range bound: {message}"),
389 }
390 }
391}
392
393#[cfg(feature = "std")]
394impl std::error::Error for InvalidPriorityRange {}
395
396impl FromStr for PriorityRange {
397 type Err = InvalidPriorityRange;
398
399 fn from_str(s: &str) -> Result<Self, Self::Err> {
400 const SEPARATOR: &str = "-";
401 let mut metadata = s.split(SEPARATOR);
402
403 let start = metadata
404 .next()
405 .ok_or_else(|| InvalidPriorityRange::InvalidSyntax {
406 found: s.to_string(),
407 })?
408 .parse::<u8>()
409 .map(Priority::try_from)
410 .map_err(|err| InvalidPriorityRange::InvalidBound {
411 message: err.to_string(),
412 })?
413 .map_err(|err| InvalidPriorityRange::InvalidBound {
414 message: err.to_string(),
415 })?;
416
417 match metadata.next() {
418 Some(slice) => {
419 let end = slice
420 .parse::<u8>()
421 .map(Priority::try_from)
422 .map_err(|err| InvalidPriorityRange::InvalidBound {
423 message: err.to_string(),
424 })?
425 .map_err(|err| InvalidPriorityRange::InvalidBound {
426 message: err.to_string(),
427 })?;
428
429 if metadata.next().is_some() {
430 return Err(InvalidPriorityRange::InvalidSyntax {
431 found: s.to_string(),
432 });
433 };
434
435 Ok(PriorityRange::new(start..=end))
436 }
437 None => Ok(PriorityRange::new(start..=start)),
438 }
439 }
440}
441
442impl Priority {
443 pub const DEFAULT: Self = Self::Data;
445 pub const MIN: Self = Self::Background;
447 pub const MAX: Self = Self::Control;
449 pub const NUM: usize = 1 + Self::MIN as usize - Self::MAX as usize;
451}
452
453impl TryFrom<u8> for Priority {
454 type Error = zenoh_result::Error;
455
456 fn try_from(v: u8) -> Result<Self, Self::Error> {
457 match v {
458 0 => Ok(Priority::Control),
459 1 => Ok(Priority::RealTime),
460 2 => Ok(Priority::InteractiveHigh),
461 3 => Ok(Priority::InteractiveLow),
462 4 => Ok(Priority::DataHigh),
463 5 => Ok(Priority::Data),
464 6 => Ok(Priority::DataLow),
465 7 => Ok(Priority::Background),
466 unknown => bail!(
467 "{} is not a valid priority value. Admitted values are: [{}-{}].",
468 unknown,
469 Self::MAX as u8,
470 Self::MIN as u8
471 ),
472 }
473 }
474}
475
476#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
478#[repr(u8)]
479pub enum Reliability {
480 BestEffort = 0,
482 #[default]
484 Reliable = 1,
485}
486
487impl Reliability {
488 pub const DEFAULT: Self = Self::Reliable;
489
490 #[cfg(feature = "test")]
491 #[doc(hidden)]
492 pub fn rand() -> Self {
493 use rand::Rng;
494
495 let mut rng = rand::thread_rng();
496
497 if rng.gen_bool(0.5) {
498 Reliability::Reliable
499 } else {
500 Reliability::BestEffort
501 }
502 }
503}
504
505impl From<bool> for Reliability {
506 fn from(value: bool) -> Self {
507 if value {
508 Reliability::Reliable
509 } else {
510 Reliability::BestEffort
511 }
512 }
513}
514
515impl From<Reliability> for bool {
516 fn from(value: Reliability) -> Self {
517 match value {
518 Reliability::BestEffort => false,
519 Reliability::Reliable => true,
520 }
521 }
522}
523
524impl Display for Reliability {
525 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526 write!(f, "{}", *self as u8)
527 }
528}
529
530#[derive(Debug)]
531pub struct InvalidReliability {
532 found: String,
533}
534
535impl Display for InvalidReliability {
536 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
537 write!(
538 f,
539 "invalid Reliability string, expected `{}` or `{}` but found {}",
540 Reliability::Reliable as u8,
541 Reliability::BestEffort as u8,
542 self.found
543 )
544 }
545}
546
547#[cfg(feature = "std")]
548impl std::error::Error for InvalidReliability {}
549
550impl FromStr for Reliability {
551 type Err = InvalidReliability;
552
553 fn from_str(s: &str) -> Result<Self, Self::Err> {
554 let Ok(desc) = s.parse::<u8>() else {
555 return Err(InvalidReliability {
556 found: s.to_string(),
557 });
558 };
559
560 if desc == Reliability::BestEffort as u8 {
561 Ok(Reliability::BestEffort)
562 } else if desc == Reliability::Reliable as u8 {
563 Ok(Reliability::Reliable)
564 } else {
565 Err(InvalidReliability {
566 found: s.to_string(),
567 })
568 }
569 }
570}
571
572#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
573pub struct Channel {
574 pub priority: Priority,
575 pub reliability: Reliability,
576}
577
578impl Channel {
579 pub const DEFAULT: Self = Self {
580 priority: Priority::DEFAULT,
581 reliability: Reliability::DEFAULT,
582 };
583}
584
585#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, Deserialize)]
587#[repr(u8)]
588pub enum CongestionControl {
589 #[default]
590 Drop = 0,
592 Block = 1,
595 #[cfg(feature = "unstable")]
596 BlockFirst = 2,
600}
601
602impl CongestionControl {
603 pub const DEFAULT: Self = Self::Drop;
604
605 #[cfg(feature = "internal")]
606 pub const DEFAULT_PUSH: Self = Self::Drop;
607 #[cfg(not(feature = "internal"))]
608 pub(crate) const DEFAULT_PUSH: Self = Self::Drop;
609
610 #[cfg(feature = "internal")]
611 pub const DEFAULT_REQUEST: Self = Self::Block;
612 #[cfg(not(feature = "internal"))]
613 pub(crate) const DEFAULT_REQUEST: Self = Self::Block;
614
615 #[cfg(feature = "internal")]
616 pub const DEFAULT_RESPONSE: Self = Self::Block;
617 #[cfg(not(feature = "internal"))]
618 pub(crate) const DEFAULT_RESPONSE: Self = Self::Block;
619
620 #[cfg(feature = "internal")]
621 pub const DEFAULT_DECLARE: Self = Self::Block;
622 #[cfg(not(feature = "internal"))]
623 pub(crate) const DEFAULT_DECLARE: Self = Self::Block;
624
625 #[cfg(feature = "internal")]
626 pub const DEFAULT_OAM: Self = Self::Block;
627 #[cfg(not(feature = "internal"))]
628 pub(crate) const DEFAULT_OAM: Self = Self::Block;
629}
630
631#[cfg(test)]
632mod tests {
633 use core::str::FromStr;
634
635 use crate::core::{Priority, PriorityRange};
636
637 #[test]
638 fn test_priority_range() {
639 assert_eq!(
640 PriorityRange::from_str("2-3"),
641 Ok(PriorityRange::new(
642 Priority::InteractiveHigh..=Priority::InteractiveLow
643 ))
644 );
645
646 assert_eq!(
647 PriorityRange::from_str("7"),
648 Ok(PriorityRange::new(
649 Priority::Background..=Priority::Background
650 ))
651 );
652
653 assert!(PriorityRange::from_str("1-").is_err());
654 assert!(PriorityRange::from_str("-5").is_err());
655 }
656}