Skip to content

Commit 40b1aa5

Browse files
committed
Add new node name-related functions
- 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 6d11125 commit 40b1aa5

File tree

2 files changed

+102
-3
lines changed

2 files changed

+102
-3
lines changed

rclrs/src/context.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,23 @@ 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(&self, node_namespace: &str, node_name: &str) -> Result<Node, RclReturnCode> {
131+
Node::new_with_namespace(node_namespace, node_name, self)
132+
}
133+
117134
/// Checks if the context is still valid.
118135
///
119136
/// 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+
/// ```
100+
/// # use rclrs::{Context, RclReturnCode};
101+
/// // Without remapping
102+
/// let context = Context::new([])?;
103+
/// let node = context.create_node("my_node")?;
104+
/// assert_eq!(node.name(), String::from("my_node"));
105+
/// // With remapping
106+
/// let remapping = ["--ros-args", "-r", "__node:=your_node"].map(String::from);
107+
/// let context_r = Context::new(remapping)?;
108+
/// let node_r = context_r.create_node("my_node")?;
109+
/// assert_eq!(node_r.name(), String::from("your_node"));
110+
/// # Ok::<(), RclReturnCode>(())
111+
/// ```
112+
pub fn name(&self) -> String {
113+
self.get_string(rcl_node_get_name)
114+
}
115+
116+
/// Returns the namespace of the node.
117+
///
118+
/// This returns the namespace after remapping, so it is not necessarily the same as the
119+
/// namespace that was used when creating the node.
120+
///
121+
/// # Example
122+
///
123+
/// ```
124+
/// # use rclrs::{Context, RclReturnCode};
125+
/// // Without remapping
126+
/// let context = Context::new([])?;
127+
/// let node = context.create_node_with_namespace("/my/namespace", "my_node")?;
128+
/// assert_eq!(node.namespace(), String::from("/my/namespace"));
129+
/// // With remapping
130+
/// let remapping = ["--ros-args", "-r", "__ns:=/your_namespace"].map(String::from);
131+
/// let context_r = Context::new(remapping)?;
132+
/// let node_r = context_r.create_node("my_node")?;
133+
/// assert_eq!(node_r.namespace(), String::from("/your_namespace"));
134+
/// # Ok::<(), RclReturnCode>(())
135+
/// ```
136+
pub fn namespace(&self) -> String {
137+
self.get_string(rcl_node_get_namespace)
138+
}
139+
140+
/// Returns the fully qualified name of the node.
141+
///
142+
/// The fully qualified name of the node is the node namespace combined with the node name.
143+
/// It is subject to the remappings shown in [`Node::name`] and [`Node::namespace`].
144+
///
145+
/// # Example
146+
///
147+
/// ```
148+
/// # use rclrs::{Context, RclReturnCode};
149+
/// let context = Context::new([])?;
150+
/// let node = context.create_node_with_namespace("/my/namespace", "my_node")?;
151+
/// assert_eq!(node.fully_qualified_name(), String::from("/my/namespace/my_node"));
152+
/// # Ok::<(), RclReturnCode>(())
153+
/// ```
154+
pub fn fully_qualified_name(&self) -> String {
155+
self.get_string(rcl_node_get_fully_qualified_name)
156+
}
157+
158+
fn get_string(&self, getter: unsafe extern "C" fn(*const rcl_node_t) -> *const c_char) -> 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)