Skip to content

Commit 0083673

Browse files
committed
Improve docs for node creation
1 parent 8edf0cc commit 0083673

File tree

4 files changed

+89
-17
lines changed

4 files changed

+89
-17
lines changed

rclrs/src/context.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,35 +97,39 @@ impl Context {
9797
Ok(context)
9898
}
9999

100-
/// Creates a node.
100+
/// Creates a new node in the empty namespace.
101101
///
102102
/// Convenience function equivalent to [`Node::new`][1].
103+
/// Please see that function's documentation.
103104
///
104105
/// [1]: crate::Node::new
105106
///
106107
/// # Example
107108
/// ```
108-
/// # use rclrs::Context;
109-
/// let ctx = Context::new([]).unwrap();
109+
/// # use rclrs::{Context, RclReturnCode};
110+
/// let ctx = Context::new([])?;
110111
/// let node = ctx.create_node("my_node");
111112
/// assert!(node.is_ok());
113+
/// # Ok::<(), RclReturnCode>(())
112114
/// ```
113115
pub fn create_node(&self, node_name: &str) -> Result<Node, RclReturnCode> {
114116
Node::new(node_name, self)
115117
}
116118

117-
/// Creates a node in a namespace.
119+
/// Creates a new node in a namespace.
118120
///
119121
/// Convenience function equivalent to [`Node::new_with_namespace`][1].
122+
/// Please see that function's documentation.
120123
///
121124
/// [1]: crate::Node::new_with_namespace
122125
///
123126
/// # Example
124127
/// ```
125-
/// # use rclrs::Context;
126-
/// let ctx = Context::new([]).unwrap();
128+
/// # use rclrs::{Context, RclReturnCode};
129+
/// let ctx = Context::new([])?;
127130
/// let node = ctx.create_node_with_namespace("/my/nested/namespace", "my_node");
128131
/// assert!(node.is_ok());
132+
/// # Ok::<(), RclReturnCode>(())
129133
/// ```
130134
pub fn create_node_with_namespace(
131135
&self,

rclrs/src/node/mod.rs

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
use crate::error::{RclReturnCode, ToResult};
2-
use crate::qos::QoSProfile;
3-
use crate::rcl_bindings::*;
4-
use crate::Context;
5-
61
mod publisher;
72
mod subscription;
83
pub use self::publisher::*;
94
pub use self::subscription::*;
105

6+
use crate::rcl_bindings::*;
7+
use crate::{Context, QoSProfile, RclReturnCode, ToResult};
118
use std::ffi::{CStr, CString};
9+
10+
use std::cmp::PartialEq;
11+
use std::fmt;
1212
use std::sync::{Arc, Weak};
1313
use std::vec::Vec;
1414

@@ -40,8 +40,26 @@ pub struct Node {
4040
pub(crate) subscriptions: Vec<Weak<dyn SubscriptionBase>>,
4141
}
4242

43+
impl Eq for Node {}
44+
45+
impl PartialEq for Node {
46+
fn eq(&self, other: &Self) -> bool {
47+
Arc::ptr_eq(&self.handle, &other.handle)
48+
}
49+
}
50+
51+
impl fmt::Debug for Node {
52+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
53+
f.debug_struct("Node")
54+
.field("fully_qualified_name", &self.fully_qualified_name())
55+
.finish()
56+
}
57+
}
58+
4359
impl Node {
4460
/// Creates a new node in the empty namespace.
61+
///
62+
/// See [`Node::new_with_namespace`] for documentation.
4563
#[allow(clippy::new_ret_no_self)]
4664
pub fn new(node_name: &str, context: &Context) -> Result<Node, RclReturnCode> {
4765
Self::new_with_namespace("", node_name, context)
@@ -51,6 +69,53 @@ impl Node {
5169
///
5270
/// A namespace without a leading forward slash is automatically changed to have a leading
5371
/// forward slash.
72+
///
73+
/// # Naming
74+
/// The node namespace will be prefixed to the node name itself to form the *fully qualified
75+
/// node name*. This is the name that is shown e.g. in `ros2 node list`.
76+
/// Similarly, the node namespace will be prefixed to all names of topics and services
77+
/// created from this node.
78+
///
79+
/// By convention, a node name with a leading underscore marks the node as hidden.
80+
///
81+
/// It's a good idea for node names in the same executable to be unique.
82+
///
83+
/// ## Remapping
84+
/// The namespace and name given here can be overriden through the command line.
85+
/// In that sense, the parameters to this functions are only the _default_ namespace and name.
86+
/// See also the [official tutorial][1] on the command line arguments for ROS nodes, and the
87+
/// [`Node::namespace()`] and [`Node::name()`] functions for examples.
88+
///
89+
/// ## Rules for valid names
90+
/// The node namespace needs to fulfill the criteria of
91+
/// [`rmw_validate_namespace()`][2], otherwise an error will be returned.
92+
/// Likewise, the node name needs to fulfill the criteria of [`rmw_validate_node_name()`][3].
93+
///
94+
/// # Example
95+
/// ```
96+
/// # use rclrs::{Context, Node, RclReturnCode, NodeErrorCode};
97+
/// let ctx = Context::new([])?;
98+
/// // This is a valid namespace and name
99+
/// assert!(Node::new_with_namespace("/my/nested/namespace", "my_node", &ctx).is_ok());
100+
/// // This name contains invalid characters, although the empty namespace is valid
101+
/// assert_eq!(
102+
/// Node::new_with_namespace("", "röböt", &ctx),
103+
/// Err(RclReturnCode::NodeError(NodeErrorCode::NodeInvalidName))
104+
/// );
105+
/// // This namespace starts with a number
106+
/// assert_eq!(
107+
/// Node::new_with_namespace("/123/4", "my_node", &ctx),
108+
/// Err(RclReturnCode::NodeError(NodeErrorCode::NodeInvalidNamespace))
109+
/// );
110+
/// # Ok::<(), RclReturnCode>(())
111+
/// ```
112+
///
113+
/// # Panics
114+
/// When the node namespace or node name contain interior null bytes.
115+
///
116+
/// [1]: https://docs.ros.org/en/rolling/How-To-Guides/Node-arguments.html
117+
/// [2]: https://docs.ros2.org/latest/api/rmw/validate__namespace_8h.html
118+
/// [3]: https://docs.ros2.org/latest/api/rmw/validate__node__name_8h.html
54119
pub fn new_with_namespace(
55120
node_ns: &str,
56121
node_name: &str,
@@ -100,12 +165,12 @@ impl Node {
100165
/// // Without remapping
101166
/// let context = Context::new([])?;
102167
/// let node = context.create_node("my_node")?;
103-
/// assert_eq!(node.name(), String::from("my_node"));
168+
/// assert_eq!(node.name(), "my_node");
104169
/// // With remapping
105170
/// let remapping = ["--ros-args", "-r", "__node:=your_node"].map(String::from);
106171
/// let context_r = Context::new(remapping)?;
107172
/// let node_r = context_r.create_node("my_node")?;
108-
/// assert_eq!(node_r.name(), String::from("your_node"));
173+
/// assert_eq!(node_r.name(), "your_node");
109174
/// # Ok::<(), RclReturnCode>(())
110175
/// ```
111176
pub fn name(&self) -> String {
@@ -123,12 +188,12 @@ impl Node {
123188
/// // Without remapping
124189
/// let context = Context::new([])?;
125190
/// let node = context.create_node_with_namespace("/my/namespace", "my_node")?;
126-
/// assert_eq!(node.namespace(), String::from("/my/namespace"));
191+
/// assert_eq!(node.namespace(), "/my/namespace");
127192
/// // With remapping
128193
/// let remapping = ["--ros-args", "-r", "__ns:=/your_namespace"].map(String::from);
129194
/// let context_r = Context::new(remapping)?;
130195
/// let node_r = context_r.create_node("my_node")?;
131-
/// assert_eq!(node_r.namespace(), String::from("/your_namespace"));
196+
/// assert_eq!(node_r.namespace(), "/your_namespace");
132197
/// # Ok::<(), RclReturnCode>(())
133198
/// ```
134199
pub fn namespace(&self) -> String {
@@ -145,13 +210,14 @@ impl Node {
145210
/// # use rclrs::{Context, RclReturnCode};
146211
/// let context = Context::new([])?;
147212
/// let node = context.create_node_with_namespace("/my/namespace", "my_node")?;
148-
/// assert_eq!(node.fully_qualified_name(), String::from("/my/namespace/my_node"));
213+
/// assert_eq!(node.fully_qualified_name(), "/my/namespace/my_node");
149214
/// # Ok::<(), RclReturnCode>(())
150215
/// ```
151216
pub fn fully_qualified_name(&self) -> String {
152217
self.get_string(rcl_node_get_fully_qualified_name)
153218
}
154219

220+
// Helper for name(), namespace(), fully_qualified_name()
155221
fn get_string(
156222
&self,
157223
getter: unsafe extern "C" fn(*const rcl_node_t) -> *const c_char,

rclrs/src/node/publisher.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ where
5858
{
5959
/// Creates a new `Publisher`.
6060
///
61+
/// Node and namespace changes are always applied _before_ topic remapping.
62+
///
6163
/// # Panics
6264
/// When the topic contains interior null bytes.
6365
pub fn new(node: &Node, topic: &str, qos: QoSProfile) -> Result<Self, RclReturnCode>

rclrs_examples/src/minimal_publisher.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::env;
44
fn main() -> Result<(), Error> {
55
let context = rclrs::Context::new(env::args())?;
66

7-
let node = context.create_node("minimal_publisher")?;
7+
let node = rclrs::Node::new_with_namespace("my/namespace", "minimal_publisher", &context)?;
88

99
let publisher =
1010
node.create_publisher::<std_msgs::msg::String>("topic", rclrs::QOS_PROFILE_DEFAULT)?;

0 commit comments

Comments
 (0)