@@ -13,6 +13,9 @@ use std::sync::Arc;
13
13
///
14
14
/// The default values for optional fields are:
15
15
/// - `namespace: "/"`
16
+ /// - `use_global_arguments: true`
17
+ /// - `arguments: []`
18
+ /// - `enable_rosout: true`
16
19
///
17
20
/// # Example
18
21
/// ```
@@ -35,11 +38,13 @@ use std::sync::Arc;
35
38
///
36
39
/// [1]: crate::Node
37
40
/// [2]: crate::Node::builder
38
- ///
39
41
pub struct NodeBuilder {
40
42
context : Arc < Mutex < rcl_context_t > > ,
41
43
name : String ,
42
44
namespace : String ,
45
+ use_global_arguments : bool ,
46
+ arguments : Vec < String > ,
47
+ enable_rosout : bool ,
43
48
}
44
49
45
50
impl NodeBuilder {
@@ -84,6 +89,9 @@ impl NodeBuilder {
84
89
context : context. handle . clone ( ) ,
85
90
name : name. to_string ( ) ,
86
91
namespace : "/" . to_string ( ) ,
92
+ use_global_arguments : true ,
93
+ arguments : vec ! [ ] ,
94
+ enable_rosout : true ,
87
95
}
88
96
}
89
97
@@ -142,6 +150,81 @@ impl NodeBuilder {
142
150
self
143
151
}
144
152
153
+ /// Enables or disables using global arguments.
154
+ ///
155
+ /// The "global" arguments are those used in [creating the context][1].
156
+ ///
157
+ /// # Example
158
+ /// ```
159
+ /// # use rclrs::{Context, Node, NodeBuilder, RclrsError};
160
+ /// let context_args = ["--ros-args", "--remap", "__node:=your_node"]
161
+ /// .map(String::from);
162
+ /// let context = Context::new(context_args)?;
163
+ /// // Ignore the global arguments:
164
+ /// let node_without_global_args = context
165
+ /// .create_node_builder("my_node")
166
+ /// .use_global_arguments(false)
167
+ /// .build()?;
168
+ /// assert_eq!(node_without_global_args.name(), "my_node");
169
+ /// // Do not ignore the global arguments:
170
+ /// let node_with_global_args = context
171
+ /// .create_node_builder("my_other_node")
172
+ /// .use_global_arguments(true)
173
+ /// .build()?;
174
+ /// assert_eq!(node_with_global_args.name(), "your_node");
175
+ /// # Ok::<(), RclrsError>(())
176
+ /// ```
177
+ ///
178
+ /// [1]: crate::Context::new
179
+ pub fn use_global_arguments ( mut self , enable : bool ) -> Self {
180
+ self . use_global_arguments = enable;
181
+ self
182
+ }
183
+
184
+ /// Sets node-specific command line arguments.
185
+ ///
186
+ /// These arguments are parsed the same way as those for [`Context::new()`][1].
187
+ /// However, the node-specific command line arguments have higher precedence than the arguments
188
+ /// used in creating the context.
189
+ ///
190
+ /// For more details about command line arguments, see [here][2].
191
+ ///
192
+ /// # Example
193
+ /// ```
194
+ /// # use rclrs::{Context, Node, NodeBuilder, RclrsError};
195
+ /// // Usually, this would change the name of "my_node" to "context_args_node":
196
+ /// let context_args = ["--ros-args", "--remap", "my_node:__node:=context_args_node"]
197
+ /// .map(String::from);
198
+ /// let context = Context::new(context_args)?;
199
+ /// // But the node arguments will change it to "node_args_node":
200
+ /// let node_args = ["--ros-args", "--remap", "my_node:__node:=node_args_node"]
201
+ /// .map(String::from);
202
+ /// let node = context
203
+ /// .create_node_builder("my_node")
204
+ /// .arguments(node_args)
205
+ /// .build()?;
206
+ /// assert_eq!(node.name(), "node_args_node");
207
+ /// # Ok::<(), RclrsError>(())
208
+ /// ```
209
+ ///
210
+ /// [1]: crate::Context::new
211
+ /// [2]: https://design.ros2.org/articles/ros_command_line_arguments.html
212
+ pub fn arguments ( mut self , arguments : impl IntoIterator < Item = String > ) -> Self {
213
+ self . arguments = arguments. into_iter ( ) . collect ( ) ;
214
+ self
215
+ }
216
+
217
+ /// Enables or disables logging to rosout.
218
+ ///
219
+ /// When enabled, log messages are published to the `/rosout` topic in addition to
220
+ /// standard output.
221
+ ///
222
+ /// This option is currently unused in `rclrs`.
223
+ pub fn enable_rosout ( mut self , enable : bool ) -> Self {
224
+ self . enable_rosout = enable;
225
+ self
226
+ }
227
+
145
228
/// Builds the node instance.
146
229
///
147
230
/// Node name and namespace validation is performed in this method.
@@ -160,16 +243,12 @@ impl NodeBuilder {
160
243
err,
161
244
s : self . namespace . clone ( ) ,
162
245
} ) ?;
246
+ let node_options = self . create_node_options ( ) ?;
247
+ let context_handle = & mut * self . context . lock ( ) ;
163
248
164
- // SAFETY: No preconditions for this function .
249
+ // SAFETY: Getting a zero-initialized value is always safe .
165
250
let mut node_handle = unsafe { rcl_get_zero_initialized_node ( ) } ;
166
-
167
251
unsafe {
168
- // SAFETY: No preconditions for this function.
169
- let context_handle = & mut * self . context . lock ( ) ;
170
- // SAFETY: No preconditions for this function.
171
- let node_options = rcl_node_get_default_options ( ) ;
172
-
173
252
// SAFETY: The node handle is zero-initialized as expected by this function.
174
253
// The strings and node options are copied by this function, so we don't need
175
254
// to keep them alive.
@@ -192,4 +271,54 @@ impl NodeBuilder {
192
271
subscriptions : std:: vec![ ] ,
193
272
} )
194
273
}
274
+
275
+ /// Creates node options.
276
+ ///
277
+ /// Any fields not present in the builder will have their default value.
278
+ /// For detail about default values, see [`NodeBuilder`][1] docs.
279
+ ///
280
+ /// [1]: crate::NodeBuilder
281
+ fn create_node_options ( & self ) -> Result < rcl_node_options_t , RclrsError > {
282
+ // SAFETY: No preconditions for this function.
283
+ let mut node_options = unsafe { rcl_node_get_default_options ( ) } ;
284
+
285
+ let cstring_args = self
286
+ . arguments
287
+ . iter ( )
288
+ . map ( |s| match CString :: new ( s. as_str ( ) ) {
289
+ Ok ( cstr) => Ok ( cstr) ,
290
+ Err ( err) => Err ( RclrsError :: StringContainsNul { s : s. clone ( ) , err } ) ,
291
+ } )
292
+ . collect :: < Result < Vec < _ > , _ > > ( ) ?;
293
+
294
+ let cstring_arg_ptrs = cstring_args. iter ( ) . map ( |s| s. as_ptr ( ) ) . collect :: < Vec < _ > > ( ) ;
295
+ unsafe {
296
+ // SAFETY: This function does not store the ephemeral cstring_args_ptrs
297
+ // pointers. We are passing in a zero-initialized arguments struct as expected.
298
+ rcl_parse_arguments (
299
+ cstring_arg_ptrs. len ( ) as i32 ,
300
+ cstring_arg_ptrs. as_ptr ( ) ,
301
+ rcutils_get_default_allocator ( ) ,
302
+ & mut node_options. arguments ,
303
+ )
304
+ }
305
+ . ok ( ) ?;
306
+
307
+ node_options. use_global_arguments = self . use_global_arguments ;
308
+ node_options. enable_rosout = self . enable_rosout ;
309
+ // SAFETY: No preconditions for this function.
310
+ node_options. allocator = unsafe { rcutils_get_default_allocator ( ) } ;
311
+
312
+ Ok ( node_options)
313
+ }
314
+ }
315
+
316
+ impl Drop for rcl_node_options_t {
317
+ fn drop ( & mut self ) {
318
+ // SAFETY: Do not finish this struct except here.
319
+ unsafe {
320
+ // This also finalizes the `rcl_arguments_t` contained in `rcl_node_options_t`.
321
+ rcl_node_options_fini ( self ) . ok ( ) . unwrap ( ) ;
322
+ }
323
+ }
195
324
}
0 commit comments