Skip to main content

ros2_client/
distributions.rs

1//! ROS 2 distribution identification.
2//!
3//! The distribution this crate is compiled for is selected via the Cargo
4//! feature chain (see `Cargo.toml`): each distribution feature enables all
5//! older ones, so `cfg!(feature = "X")` means "at least distribution X".
6//!
7//! [`COMPILED_ROS_DISTRO`] exposes the selected distribution as a runtime
8//! constant, and [`crate::distributions::verify_ros_distro_env`] compares it
9//! against the `ROS_DISTRO` environment variable at [`crate::Context`]
10//! initialization.
11
12use std::fmt;
13
14#[allow(unused_imports)]
15use log::{error, info, warn};
16
17/// A ROS 2 distribution.
18///
19/// Ordered chronologically, so comparisons (`<`, `>=`, ...) reflect release
20/// order. Marked `#[non_exhaustive]` because future distributions will be
21/// added.
22#[non_exhaustive]
23#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub enum RosDistro {
25  Galactic,
26  Humble,
27  Iron,
28  Jazzy,
29  Kilted,
30  Lyrical,
31}
32
33impl RosDistro {
34  /// The lower-case distribution name, matching the `ROS_DISTRO` value ROS 2
35  /// sets when its environment is sourced (e.g. `"jazzy"`).
36  pub const fn as_str(self) -> &'static str {
37    match self {
38      RosDistro::Galactic => "galactic",
39      RosDistro::Humble => "humble",
40      RosDistro::Iron => "iron",
41      RosDistro::Jazzy => "jazzy",
42      RosDistro::Kilted => "kilted",
43      RosDistro::Lyrical => "lyrical",
44    }
45  }
46}
47
48impl fmt::Display for RosDistro {
49  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50    f.write_str(self.as_str())
51  }
52}
53
54/// The ROS 2 distribution this crate was compiled for, i.e. the highest
55/// activated distribution feature in the chain.
56pub const COMPILED_ROS_DISTRO: RosDistro = compiled_distro();
57
58const fn compiled_distro() -> RosDistro {
59  // Features are additive and each enables all older ones, so the newest
60  // enabled feature identifies the selected distribution.
61  if cfg!(feature = "lyrical") {
62    RosDistro::Lyrical
63  } else if cfg!(feature = "kilted") {
64    RosDistro::Kilted
65  } else if cfg!(feature = "jazzy") {
66    RosDistro::Jazzy
67  } else if cfg!(feature = "iron") {
68    RosDistro::Iron
69  } else if cfg!(feature = "humble") {
70    RosDistro::Humble
71  } else {
72    RosDistro::Galactic
73  }
74}
75
76/// Compare the `ROS_DISTRO` environment variable against the distribution this
77/// crate was compiled for, and log the outcome:
78///
79/// * unset -> warning (we cannot tell whether the build matches the runtime),
80/// * equal -> info (all good),
81/// * `"rolling"` -> warning (rolling tracks the newest, assume compatible),
82/// * anything else -> error (likely wire-incompatible, e.g. wrong Gid format).
83pub fn verify_ros_distro_env() {
84  let built = COMPILED_ROS_DISTRO;
85  match std::env::var("ROS_DISTRO") {
86    Err(_) => warn!("ROS_DISTRO is not set; ros2-client was built for '{built}'."),
87    Ok(distro) if distro == built.as_str() => {
88      info!("ROS_DISTRO='{distro}' matches the ros2-client build.");
89    }
90    Ok(distro) if distro == "rolling" => {
91      warn!(
92        "ROS_DISTRO='rolling'; ros2-client was built for '{built}'. Assuming compatible, but \
93         rolling may have diverged."
94      );
95    }
96    Ok(distro) => {
97      error!(
98        "ROS_DISTRO='{distro}' but ros2-client was built for '{built}'. These may be \
99         incompatible (e.g. different Gid format); rebuild with --features {distro}."
100      );
101    }
102  }
103}