Skip to content

Commit 4b2b023

Browse files
authored
Add new node name-related functions (#143)
- Add Node::name, Node::namespace, Node::fully_qualified_name getters - Add Context::create_node_with_namespace function - Swap order of namespace and name argument in Node::new_with_namespace, so that it's less confusing
1 parent 16d2ff3 commit 4b2b023

File tree

2 files changed

+106
-3
lines changed

2 files changed

+106
-3
lines changed

rclrs/src/context.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,27 @@ impl Context {
114114
Node::new(node_name, self)
115115
}
116116

117+
/// Creates a node in a namespace.
118+
///
119+
/// Convenience function equivalent to [`Node::new_with_namespace`][1].
120+
///
121+
/// [1]: crate::Node::new_with_namespace
122+
///
123+
/// # Example
124+
/// ```
125+
/// # use rclrs::Context;
126+
/// let ctx = Context::new([]).unwrap();
127+
/// let node = ctx.create_node_with_namespace("/my/nested/namespace", "my_node");
128+
/// assert!(node.is_ok());
129+
/// ```
130+
pub fn create_node_with_namespace(
131+
&self,
132+
node_namespace: &str,
133+
node_name: &str,
134+
) -> Result<Node, RclReturnCode> {
135+
Node::new_with_namespace(node_namespace, node_name, self)
136+
}
137+
117138
/// Checks if the context is still valid.
118139
///
119140
/// This will return `false` when a signal has caused the context to shut down (currently

rclrs/src/node/mod.rs

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ mod subscription;
88
pub use self::publisher::*;
99
pub use self::subscription::*;
1010

11-
use std::ffi::CString;
11+
use std::ffi::{CStr, CString};
1212
use std::sync::{Arc, Weak};
1313
use std::vec::Vec;
1414

15+
use libc::c_char;
1516
use parking_lot::Mutex;
1617

1718
use rosidl_runtime_rs::Message;
@@ -43,16 +44,16 @@ impl Node {
4344
/// Creates a new node in the empty namespace.
4445
#[allow(clippy::new_ret_no_self)]
4546
pub fn new(node_name: &str, context: &Context) -> Result<Node, RclReturnCode> {
46-
Self::new_with_namespace(node_name, "", context)
47+
Self::new_with_namespace("", node_name, context)
4748
}
4849

4950
/// Creates a new node in a namespace.
5051
///
5152
/// A namespace without a leading forward slash is automatically changed to have a leading
5253
/// forward slash.
5354
pub fn new_with_namespace(
54-
node_name: &str,
5555
node_ns: &str,
56+
node_name: &str,
5657
context: &Context,
5758
) -> Result<Node, RclReturnCode> {
5859
let raw_node_name = CString::new(node_name).unwrap();
@@ -88,6 +89,87 @@ impl Node {
8889
})
8990
}
9091

92+
/// Returns the name of the node.
93+
///
94+
/// This returns the name after remapping, so it is not necessarily the same as the name that
95+
/// was used when creating the node.
96+
///
97+
/// # Example
98+
/// ```
99+
/// # use rclrs::{Context, RclReturnCode};
100+
/// // Without remapping
101+
/// let context = Context::new([])?;
102+
/// let node = context.create_node("my_node")?;
103+
/// assert_eq!(node.name(), String::from("my_node"));
104+
/// // With remapping
105+
/// let remapping = ["--ros-args", "-r", "__node:=your_node"].map(String::from);
106+
/// let context_r = Context::new(remapping)?;
107+
/// let node_r = context_r.create_node("my_node")?;
108+
/// assert_eq!(node_r.name(), String::from("your_node"));
109+
/// # Ok::<(), RclReturnCode>(())
110+
/// ```
111+
pub fn name(&self) -> String {
112+
self.get_string(rcl_node_get_name)
113+
}
114+
115+
/// Returns the namespace of the node.
116+
///
117+
/// This returns the namespace after remapping, so it is not necessarily the same as the
118+
/// namespace that was used when creating the node.
119+
///
120+
/// # Example
121+
/// ```
122+
/// # use rclrs::{Context, RclReturnCode};
123+
/// // Without remapping
124+
/// let context = Context::new([])?;
125+
/// let node = context.create_node_with_namespace("/my/namespace", "my_node")?;
126+
/// assert_eq!(node.namespace(), String::from("/my/namespace"));
127+
/// // With remapping
128+
/// let remapping = ["--ros-args", "-r", "__ns:=/your_namespace"].map(String::from);
129+
/// let context_r = Context::new(remapping)?;
130+
/// let node_r = context_r.create_node("my_node")?;
131+
/// assert_eq!(node_r.namespace(), String::from("/your_namespace"));
132+
/// # Ok::<(), RclReturnCode>(())
133+
/// ```
134+
pub fn namespace(&self) -> String {
135+
self.get_string(rcl_node_get_namespace)
136+
}
137+
138+
/// Returns the fully qualified name of the node.
139+
///
140+
/// The fully qualified name of the node is the node namespace combined with the node name.
141+
/// It is subject to the remappings shown in [`Node::name`] and [`Node::namespace`].
142+
///
143+
/// # Example
144+
/// ```
145+
/// # use rclrs::{Context, RclReturnCode};
146+
/// let context = Context::new([])?;
147+
/// let node = context.create_node_with_namespace("/my/namespace", "my_node")?;
148+
/// assert_eq!(node.fully_qualified_name(), String::from("/my/namespace/my_node"));
149+
/// # Ok::<(), RclReturnCode>(())
150+
/// ```
151+
pub fn fully_qualified_name(&self) -> String {
152+
self.get_string(rcl_node_get_fully_qualified_name)
153+
}
154+
155+
fn get_string(
156+
&self,
157+
getter: unsafe extern "C" fn(*const rcl_node_t) -> *const c_char,
158+
) -> String {
159+
let char_ptr = unsafe {
160+
// SAFETY: The node handle is valid.
161+
getter(&*self.handle.lock())
162+
};
163+
debug_assert!(!char_ptr.is_null());
164+
let cstr = unsafe {
165+
// SAFETY: The returned CStr is immediately converted to an owned string,
166+
// so the lifetime is no issue. The ptr is valid as per the documentation
167+
// of rcl_node_get_name.
168+
CStr::from_ptr(char_ptr)
169+
};
170+
cstr.to_string_lossy().into_owned()
171+
}
172+
91173
/// Creates a [`Publisher`][1].
92174
///
93175
/// [1]: crate::Publisher

0 commit comments

Comments
 (0)