Skip to main content

nros_node/
node_options.rs

1//! Node construction options — rclrs's `NodeOptions` / `IntoNodeOptions`
2//! (phase-515 R2).
3//!
4//! Lives here rather than in `nros` so the executor's own
5//! [`create_node`](crate::Executor::create_node) can take it; `nros`
6//! re-exports both names unchanged.
7
8/// Runtime-neutral node construction options.
9///
10/// ADOPT-BOUNDED against rclrs's `NodeOptions` / `IntoNodeOptions` builder: three
11/// public fields (`name`, `namespace`, `domain_id`) and nothing else. There are no
12/// node ARGUMENTS and no rosout toggle — both are fixed at build time here
13/// (RFC-0045), so an rclrs builder call for either is a compile error.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct NodeOptions<'a> {
16    /// Source node name. Launch planning may remap/namespace later.
17    pub name: &'a str,
18    /// Source namespace. Defaults to `/`.
19    pub namespace: &'a str,
20    /// ROS domain ID hint. Defaults to `0`.
21    pub domain_id: u32,
22}
23
24impl<'a> NodeOptions<'a> {
25    /// Create node options with default namespace and domain.
26    pub const fn new(name: &'a str) -> Self {
27        Self {
28            name,
29            namespace: "/",
30            domain_id: 0,
31        }
32    }
33
34    /// Set source namespace.
35    pub const fn namespace(mut self, namespace: &'a str) -> Self {
36        self.namespace = namespace;
37        self
38    }
39
40    /// Set ROS domain ID hint.
41    pub const fn domain_id(mut self, domain_id: u32) -> Self {
42        self.domain_id = domain_id;
43        self
44    }
45}
46
47/// rclrs's `IntoNodeOptions`: anything a node can be created from — a bare
48/// name (`"talker"`) or a full [`NodeOptions`] — so
49/// `executor.create_node("talker")` and
50/// `executor.create_node("talker".namespace("/robot"))` read as they do in
51/// rclrs.
52///
53/// ADOPT-BOUNDED: rclrs's other builder steps (`arguments`,
54/// `use_global_arguments`, `enable_rosout`, `start_parameter_services`,
55/// `clock_type`, `clock_qos`) configure what is fixed at build time here
56/// (RFC-0045), so they are absent and a call to one is a compile error.
57pub trait IntoNodeOptions<'a>: Sized {
58    /// Convert to the full options value.
59    fn into_node_options(self) -> NodeOptions<'a>;
60
61    /// Set the namespace — rclrs's `"name".namespace("/ns")`.
62    fn namespace(self, namespace: &'a str) -> NodeOptions<'a> {
63        self.into_node_options().namespace(namespace)
64    }
65}
66
67impl<'a> IntoNodeOptions<'a> for &'a str {
68    fn into_node_options(self) -> NodeOptions<'a> {
69        NodeOptions::new(self)
70    }
71}
72
73impl<'a> IntoNodeOptions<'a> for NodeOptions<'a> {
74    fn into_node_options(self) -> NodeOptions<'a> {
75        self
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn a_bare_name_takes_the_defaults() {
85        assert_eq!("talker".into_node_options(), NodeOptions::new("talker"));
86    }
87
88    #[test]
89    fn rclrs_namespace_step_on_a_bare_name() {
90        let o = "talker".namespace("/robot");
91        assert_eq!(o.name, "talker");
92        assert_eq!(o.namespace, "/robot");
93        assert_eq!(o.domain_id, 0);
94    }
95
96    #[test]
97    fn full_options_pass_through() {
98        let o = NodeOptions::new("talker").domain_id(7);
99        assert_eq!(o.into_node_options(), o);
100    }
101}