1use std::{ffi::CStr, sync::Arc};
2
3use crate::{
4 parameter::{ParameterRange, ParameterRanges},
5 rcl_bindings::*,
6 vendor::rcl_interfaces::msg::rmw::{ParameterType, ParameterValue as RmwParameterValue},
7 ParameterValueError,
8};
9
10#[derive(Clone, Debug, PartialEq)]
15pub enum ParameterValue {
16 Bool(bool),
20 Integer(i64),
24 Double(f64),
28 String(Arc<str>),
35 ByteArray(Arc<[u8]>),
39 BoolArray(Arc<[bool]>),
43 IntegerArray(Arc<[i64]>),
47 DoubleArray(Arc<[f64]>),
51 StringArray(Arc<[Arc<str>]>),
55}
56
57#[derive(Clone, Debug, PartialEq)]
60pub enum ParameterKind {
61 Bool,
63 Integer,
65 Double,
67 String,
69 ByteArray,
71 BoolArray,
73 IntegerArray,
75 DoubleArray,
77 StringArray,
79 Dynamic,
81}
82
83impl From<bool> for ParameterValue {
84 fn from(value: bool) -> ParameterValue {
85 ParameterValue::Bool(value)
86 }
87}
88
89impl From<i64> for ParameterValue {
90 fn from(value: i64) -> ParameterValue {
91 ParameterValue::Integer(value)
92 }
93}
94
95impl From<f64> for ParameterValue {
96 fn from(value: f64) -> ParameterValue {
97 ParameterValue::Double(value)
98 }
99}
100
101impl From<Arc<str>> for ParameterValue {
102 fn from(value: Arc<str>) -> ParameterValue {
103 ParameterValue::String(value)
104 }
105}
106
107impl From<Arc<[u8]>> for ParameterValue {
108 fn from(value: Arc<[u8]>) -> ParameterValue {
109 ParameterValue::ByteArray(value)
110 }
111}
112
113impl From<Arc<[bool]>> for ParameterValue {
114 fn from(value: Arc<[bool]>) -> ParameterValue {
115 ParameterValue::BoolArray(value)
116 }
117}
118
119impl From<Arc<[i64]>> for ParameterValue {
120 fn from(value: Arc<[i64]>) -> ParameterValue {
121 ParameterValue::IntegerArray(value)
122 }
123}
124
125impl From<Arc<[f64]>> for ParameterValue {
126 fn from(value: Arc<[f64]>) -> ParameterValue {
127 ParameterValue::DoubleArray(value)
128 }
129}
130
131impl From<Arc<[Arc<str>]>> for ParameterValue {
132 fn from(value: Arc<[Arc<str>]>) -> ParameterValue {
133 ParameterValue::StringArray(value)
134 }
135}
136
137pub trait ParameterVariant: Into<ParameterValue> + Clone + TryFrom<ParameterValue> {
139 type Range: Into<ParameterRanges> + Default + Clone;
141
142 fn kind() -> ParameterKind;
144}
145
146impl TryFrom<ParameterValue> for bool {
147 type Error = ParameterValueError;
148
149 fn try_from(value: ParameterValue) -> Result<Self, Self::Error> {
150 match value {
151 ParameterValue::Bool(v) => Ok(v),
152 _ => Err(ParameterValueError::TypeMismatch),
153 }
154 }
155}
156
157impl ParameterVariant for bool {
158 type Range = ();
159
160 fn kind() -> ParameterKind {
161 ParameterKind::Bool
162 }
163}
164
165impl TryFrom<ParameterValue> for i64 {
166 type Error = ParameterValueError;
167
168 fn try_from(value: ParameterValue) -> Result<Self, Self::Error> {
169 match value {
170 ParameterValue::Integer(v) => Ok(v),
171 _ => Err(ParameterValueError::TypeMismatch),
172 }
173 }
174}
175
176impl ParameterVariant for i64 {
177 type Range = ParameterRange<i64>;
178
179 fn kind() -> ParameterKind {
180 ParameterKind::Integer
181 }
182}
183
184impl TryFrom<ParameterValue> for f64 {
185 type Error = ParameterValueError;
186
187 fn try_from(value: ParameterValue) -> Result<Self, Self::Error> {
188 match value {
189 ParameterValue::Double(v) => Ok(v),
190 _ => Err(ParameterValueError::TypeMismatch),
191 }
192 }
193}
194
195impl ParameterVariant for f64 {
196 type Range = ParameterRange<f64>;
197
198 fn kind() -> ParameterKind {
199 ParameterKind::Double
200 }
201}
202
203impl TryFrom<ParameterValue> for Arc<str> {
204 type Error = ParameterValueError;
205
206 fn try_from(value: ParameterValue) -> Result<Self, Self::Error> {
207 match value {
208 ParameterValue::String(v) => Ok(v),
209 _ => Err(ParameterValueError::TypeMismatch),
210 }
211 }
212}
213
214impl ParameterVariant for Arc<str> {
215 type Range = ();
216
217 fn kind() -> ParameterKind {
218 ParameterKind::String
219 }
220}
221
222impl TryFrom<ParameterValue> for Arc<[u8]> {
223 type Error = ParameterValueError;
224
225 fn try_from(value: ParameterValue) -> Result<Self, Self::Error> {
226 match value {
227 ParameterValue::ByteArray(v) => Ok(v),
228 _ => Err(ParameterValueError::TypeMismatch),
229 }
230 }
231}
232
233impl ParameterVariant for Arc<[u8]> {
234 type Range = ();
235
236 fn kind() -> ParameterKind {
237 ParameterKind::ByteArray
238 }
239}
240
241impl TryFrom<ParameterValue> for Arc<[bool]> {
242 type Error = ParameterValueError;
243
244 fn try_from(value: ParameterValue) -> Result<Self, Self::Error> {
245 match value {
246 ParameterValue::BoolArray(v) => Ok(v),
247 _ => Err(ParameterValueError::TypeMismatch),
248 }
249 }
250}
251
252impl ParameterVariant for Arc<[bool]> {
253 type Range = ();
254
255 fn kind() -> ParameterKind {
256 ParameterKind::BoolArray
257 }
258}
259
260impl TryFrom<ParameterValue> for Arc<[i64]> {
261 type Error = ParameterValueError;
262
263 fn try_from(value: ParameterValue) -> Result<Self, Self::Error> {
264 match value {
265 ParameterValue::IntegerArray(v) => Ok(v),
266 _ => Err(ParameterValueError::TypeMismatch),
267 }
268 }
269}
270
271impl ParameterVariant for Arc<[i64]> {
272 type Range = ();
273
274 fn kind() -> ParameterKind {
275 ParameterKind::IntegerArray
276 }
277}
278
279impl TryFrom<ParameterValue> for Arc<[f64]> {
280 type Error = ParameterValueError;
281
282 fn try_from(value: ParameterValue) -> Result<Self, Self::Error> {
283 match value {
284 ParameterValue::DoubleArray(v) => Ok(v),
285 _ => Err(ParameterValueError::TypeMismatch),
286 }
287 }
288}
289
290impl ParameterVariant for Arc<[f64]> {
291 type Range = ();
292
293 fn kind() -> ParameterKind {
294 ParameterKind::DoubleArray
295 }
296}
297
298impl TryFrom<ParameterValue> for Arc<[Arc<str>]> {
299 type Error = ParameterValueError;
300
301 fn try_from(value: ParameterValue) -> Result<Self, Self::Error> {
302 match value {
303 ParameterValue::StringArray(v) => Ok(v),
304 _ => Err(ParameterValueError::TypeMismatch),
305 }
306 }
307}
308
309impl ParameterVariant for Arc<[Arc<str>]> {
310 type Range = ();
311
312 fn kind() -> ParameterKind {
313 ParameterKind::StringArray
314 }
315}
316
317impl ParameterVariant for ParameterValue {
318 type Range = ParameterRanges;
319
320 fn kind() -> ParameterKind {
321 ParameterKind::Dynamic
322 }
323}
324
325impl From<ParameterValue> for RmwParameterValue {
326 fn from(value: ParameterValue) -> Self {
327 match value {
328 ParameterValue::Bool(v) => RmwParameterValue {
329 type_: ParameterType::PARAMETER_BOOL,
330 bool_value: v,
331 ..Default::default()
332 },
333 ParameterValue::Integer(v) => RmwParameterValue {
334 type_: ParameterType::PARAMETER_INTEGER,
335 integer_value: v,
336 ..Default::default()
337 },
338 ParameterValue::Double(v) => RmwParameterValue {
339 type_: ParameterType::PARAMETER_DOUBLE,
340 double_value: v,
341 ..Default::default()
342 },
343 ParameterValue::String(v) => RmwParameterValue {
344 type_: ParameterType::PARAMETER_STRING,
345 string_value: v.into(),
346 ..Default::default()
347 },
348 ParameterValue::ByteArray(v) => RmwParameterValue {
349 type_: ParameterType::PARAMETER_BYTE_ARRAY,
350 byte_array_value: (*v).into(),
351 ..Default::default()
352 },
353 ParameterValue::BoolArray(v) => RmwParameterValue {
354 type_: ParameterType::PARAMETER_BOOL_ARRAY,
355 bool_array_value: (*v).into(),
356 ..Default::default()
357 },
358 ParameterValue::IntegerArray(v) => RmwParameterValue {
359 type_: ParameterType::PARAMETER_INTEGER_ARRAY,
360 integer_array_value: (*v).into(),
361 ..Default::default()
362 },
363 ParameterValue::DoubleArray(v) => RmwParameterValue {
364 type_: ParameterType::PARAMETER_DOUBLE_ARRAY,
365 double_array_value: (*v).into(),
366 ..Default::default()
367 },
368 ParameterValue::StringArray(v) => RmwParameterValue {
369 type_: ParameterType::PARAMETER_STRING_ARRAY,
370 string_array_value: v.iter().map(|v| v.clone().into()).collect(),
371 ..Default::default()
372 },
373 }
374 }
375}
376
377#[derive(Debug)]
380pub enum RmwParameterConversionError {
381 InvalidParameterType,
383}
384
385impl std::fmt::Display for RmwParameterConversionError {
386 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
387 match self {
388 RmwParameterConversionError::InvalidParameterType => {
389 write!(f, "the parameter type was not valid")
390 }
391 }
392 }
393}
394
395impl std::error::Error for RmwParameterConversionError {}
396
397impl TryFrom<RmwParameterValue> for ParameterValue {
398 type Error = RmwParameterConversionError;
399
400 fn try_from(param: RmwParameterValue) -> Result<Self, Self::Error> {
401 match param.type_ {
404 ParameterType::PARAMETER_BOOL => Ok(ParameterValue::Bool(param.bool_value)),
405 ParameterType::PARAMETER_INTEGER => Ok(ParameterValue::Integer(param.integer_value)),
406 ParameterType::PARAMETER_DOUBLE => Ok(ParameterValue::Double(param.double_value)),
407 ParameterType::PARAMETER_STRING => Ok(ParameterValue::String(
408 param.string_value.to_string().into(),
409 )),
410 ParameterType::PARAMETER_BYTE_ARRAY => {
411 Ok(ParameterValue::ByteArray((*param.byte_array_value).into()))
412 }
413 ParameterType::PARAMETER_BOOL_ARRAY => {
414 Ok(ParameterValue::BoolArray((*param.bool_array_value).into()))
415 }
416 ParameterType::PARAMETER_INTEGER_ARRAY => Ok(ParameterValue::IntegerArray(
417 (*param.integer_array_value).into(),
418 )),
419 ParameterType::PARAMETER_DOUBLE_ARRAY => Ok(ParameterValue::DoubleArray(
420 (*param.double_array_value).into(),
421 )),
422 ParameterType::PARAMETER_STRING_ARRAY => Ok(ParameterValue::StringArray(
423 param
424 .string_array_value
425 .iter()
426 .map(|s| s.to_string().into())
427 .collect::<Vec<_>>()
428 .into(),
429 )),
430 _ => Err(RmwParameterConversionError::InvalidParameterType),
431 }
432 }
433}
434
435impl ParameterValue {
436 pub(crate) unsafe fn from_rcl_variant(var: &rcl_variant_t) -> Self {
441 let num_active: u8 = [
442 !var.bool_value.is_null(),
443 !var.integer_value.is_null(),
444 !var.double_value.is_null(),
445 !var.string_value.is_null(),
446 !var.byte_array_value.is_null(),
447 !var.bool_array_value.is_null(),
448 !var.integer_array_value.is_null(),
449 !var.double_array_value.is_null(),
450 !var.string_array_value.is_null(),
451 ]
452 .into_iter()
453 .map(u8::from)
454 .sum();
455 assert_eq!(num_active, 1);
456 if !var.bool_value.is_null() {
466 unsafe { ParameterValue::Bool(*var.bool_value) }
467 } else if !var.integer_value.is_null() {
468 unsafe { ParameterValue::Integer(*var.integer_value) }
469 } else if !var.double_value.is_null() {
470 unsafe { ParameterValue::Double(*var.double_value) }
471 } else if !var.string_value.is_null() {
472 unsafe {
473 let cstr = CStr::from_ptr(var.string_value);
474 let s = cstr.to_string_lossy().into_owned();
475 ParameterValue::String(s.into())
476 }
477 } else if !var.byte_array_value.is_null() {
478 unsafe {
479 let rcl_byte_array = &*var.byte_array_value;
480 let slice = rcl_from_raw_parts(rcl_byte_array.values, rcl_byte_array.size);
481 ParameterValue::ByteArray(slice.into())
482 }
483 } else if !var.bool_array_value.is_null() {
484 unsafe {
485 let rcl_bool_array = &*var.bool_array_value;
486 let slice = rcl_from_raw_parts(rcl_bool_array.values, rcl_bool_array.size);
487 ParameterValue::BoolArray(slice.into())
488 }
489 } else if !var.integer_array_value.is_null() {
490 unsafe {
491 let rcl_integer_array = &*var.integer_array_value;
492 let slice = rcl_from_raw_parts(rcl_integer_array.values, rcl_integer_array.size);
493 ParameterValue::IntegerArray(slice.into())
494 }
495 } else if !var.double_array_value.is_null() {
496 unsafe {
497 let rcl_double_array = &*var.double_array_value;
498 let slice = rcl_from_raw_parts(rcl_double_array.values, rcl_double_array.size);
499 ParameterValue::DoubleArray(slice.into())
500 }
501 } else if !var.string_array_value.is_null() {
502 unsafe {
503 let rcutils_string_array = &*var.string_array_value;
504 let slice =
505 rcl_from_raw_parts(rcutils_string_array.data, rcutils_string_array.size);
506 let strings = slice
507 .iter()
508 .map(|&ptr| {
509 debug_assert!(!ptr.is_null());
510 let cstr = CStr::from_ptr(ptr);
511 Arc::from(cstr.to_string_lossy())
512 })
513 .collect::<Vec<_>>();
514 ParameterValue::StringArray(strings.into())
515 }
516 } else {
517 unreachable!()
518 }
519 }
520
521 pub(crate) fn rcl_parameter_type(&self) -> u8 {
522 match self {
523 ParameterValue::Bool(_) => ParameterType::PARAMETER_BOOL,
524 ParameterValue::Integer(_) => ParameterType::PARAMETER_INTEGER,
525 ParameterValue::Double(_) => ParameterType::PARAMETER_DOUBLE,
526 ParameterValue::String(_) => ParameterType::PARAMETER_STRING,
527 ParameterValue::ByteArray(_) => ParameterType::PARAMETER_BYTE_ARRAY,
528 ParameterValue::BoolArray(_) => ParameterType::PARAMETER_BOOL_ARRAY,
529 ParameterValue::IntegerArray(_) => ParameterType::PARAMETER_INTEGER_ARRAY,
530 ParameterValue::DoubleArray(_) => ParameterType::PARAMETER_DOUBLE_ARRAY,
531 ParameterValue::StringArray(_) => ParameterType::PARAMETER_STRING_ARRAY,
532 }
533 }
534
535 pub(crate) fn kind(&self) -> ParameterKind {
537 match self {
538 ParameterValue::Bool(_) => ParameterKind::Bool,
539 ParameterValue::Integer(_) => ParameterKind::Integer,
540 ParameterValue::Double(_) => ParameterKind::Double,
541 ParameterValue::String(_) => ParameterKind::String,
542 ParameterValue::ByteArray(_) => ParameterKind::ByteArray,
543 ParameterValue::BoolArray(_) => ParameterKind::BoolArray,
544 ParameterValue::IntegerArray(_) => ParameterKind::IntegerArray,
545 ParameterValue::DoubleArray(_) => ParameterKind::DoubleArray,
546 ParameterValue::StringArray(_) => ParameterKind::StringArray,
547 }
548 }
549}
550
551#[cfg(test)]
552mod tests {
553 use super::*;
554 use crate::{Context, InitOptions, RclrsError, ToResult};
555
556 #[test]
559 fn test_parameter_value() -> Result<(), RclrsError> {
560 let input_output_pairs = [
563 ("true", ParameterValue::Bool(true)),
564 ("1", ParameterValue::Integer(1)),
565 ("1.0", ParameterValue::Double(1.0)),
566 ("'1.0'", ParameterValue::String(Arc::from("1.0"))),
567 (
568 "[yes, no]",
569 ParameterValue::BoolArray(Arc::from([true, false])),
570 ),
571 ("[-3, 2]", ParameterValue::IntegerArray(Arc::from([-3, 2]))),
572 (
573 "[-3.0, 2.0]",
574 ParameterValue::DoubleArray(Arc::from([-3.0, 2.0])),
575 ),
576 (
577 "['yes']",
578 ParameterValue::StringArray(Arc::from([Arc::from("yes")])),
579 ),
580 ];
581 for pair in input_output_pairs {
582 let ctx = Context::new(
583 [
584 String::from("--ros-args"),
585 String::from("-p"),
586 format!("foo:={}", pair.0),
587 ],
588 InitOptions::default(),
589 )?;
590 let mut rcl_params = std::ptr::null_mut();
591 unsafe {
592 rcl_arguments_get_param_overrides(
593 &ctx.handle.rcl_context.lock().unwrap().global_arguments,
594 &mut rcl_params,
595 )
596 .ok()?;
597 }
598 assert!(!rcl_params.is_null());
599 assert_eq!(unsafe { (*rcl_params).num_nodes }, 1);
600 let rcl_node_params = unsafe { &(*(*rcl_params).params) };
601 assert_eq!(rcl_node_params.num_params, 1);
602 let rcl_variant = unsafe { &(*rcl_node_params.parameter_values) };
603 let param_value = unsafe { ParameterValue::from_rcl_variant(rcl_variant) };
604 assert_eq!(param_value, pair.1);
605 unsafe { rcl_yaml_node_struct_fini(rcl_params) };
606 }
607 Ok(())
608 }
609}