Skip to content

Commit 318bebf

Browse files
authored
Make rclrs panic-free as far as possible (#190)
This is achieved by making CString errors a part of RclrsError, since RclrsError is now an enum.
1 parent 87ad6ee commit 318bebf

File tree

5 files changed

+41
-24
lines changed

5 files changed

+41
-24
lines changed

rclrs/src/context.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,18 @@ impl Context {
5959
/// let invalid_remapping = ["--ros-args", "-r", ":=:*/]"].map(String::from);
6060
/// assert!(Context::new(invalid_remapping).is_err());
6161
/// ```
62-
///
63-
/// # Panics
64-
/// When there is an interior null byte in any of the args.
6562
pub fn new(args: impl IntoIterator<Item = String>) -> Result<Self, RclrsError> {
6663
// SAFETY: Getting a zero-initialized value is always safe
6764
let mut rcl_context = unsafe { rcl_get_zero_initialized_context() };
6865
let cstring_args: Vec<CString> = args
6966
.into_iter()
70-
.map(|arg| CString::new(arg).unwrap())
71-
.collect();
67+
.map(|arg| {
68+
CString::new(arg.as_str()).map_err(|err| RclrsError::StringContainsNul {
69+
err,
70+
s: arg.clone(),
71+
})
72+
})
73+
.collect::<Result<_, _>>()?;
7274
// Vector of pointers into cstring_args
7375
let c_args: Vec<*const c_char> = cstring_args.iter().map(|arg| arg.as_ptr()).collect();
7476
unsafe {

rclrs/src/error.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::rcl_bindings::*;
22
use std::error::Error;
3-
use std::ffi::CStr;
3+
use std::ffi::{CStr, NulError};
44
use std::fmt::{self, Display};
55

66
/// The main error type.
@@ -20,13 +20,23 @@ pub enum RclrsError {
2020
/// The error message set in the `rcl` layer or below.
2121
msg: Option<RclErrorMsg>,
2222
},
23+
/// A string provided to `rclrs` could not be converted into a `CString`.
24+
StringContainsNul {
25+
/// The string that contains a nul byte.
26+
s: String,
27+
/// The error indicating the position of the nul byte.
28+
err: NulError,
29+
},
2330
}
2431

2532
impl Display for RclrsError {
2633
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2734
match self {
2835
RclrsError::RclError { code, .. } => write!(f, "{}", code),
2936
RclrsError::UnknownRclError { code, .. } => write!(f, "{}", code),
37+
RclrsError::StringContainsNul { s, .. } => {
38+
write!(f, "Could not convert string '{}' to CString", s)
39+
}
3040
}
3141
}
3242
}
@@ -54,11 +64,11 @@ impl Error for RclErrorMsg {}
5464

5565
impl Error for RclrsError {
5666
fn source(&self) -> Option<&(dyn Error + 'static)> {
57-
let msg = match self {
58-
RclrsError::RclError { msg, .. } => msg,
59-
RclrsError::UnknownRclError { msg, .. } => msg,
60-
};
61-
msg.as_ref().map(|e| e as &dyn Error)
67+
match self {
68+
RclrsError::RclError { msg, .. } => msg.as_ref().map(|e| e as &dyn Error),
69+
RclrsError::UnknownRclError { msg, .. } => msg.as_ref().map(|e| e as &dyn Error),
70+
RclrsError::StringContainsNul { err, .. } => Some(err).map(|e| e as &dyn Error),
71+
}
6272
}
6373
}
6474

rclrs/src/node/builder.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,18 @@ impl NodeBuilder {
148148
///
149149
/// For example usage, see the [`NodeBuilder`][1] docs.
150150
///
151-
/// # Panics
152-
/// When the node name or namespace contain null bytes.
153-
///
154151
/// [1]: crate::NodeBuilder
155152
pub fn build(&self) -> Result<Node, RclrsError> {
156-
let node_name = CString::new(self.name.as_str()).unwrap();
157-
let node_namespace = CString::new(self.namespace.as_str()).unwrap();
153+
let node_name =
154+
CString::new(self.name.as_str()).map_err(|err| RclrsError::StringContainsNul {
155+
err,
156+
s: self.name.clone(),
157+
})?;
158+
let node_namespace =
159+
CString::new(self.namespace.as_str()).map_err(|err| RclrsError::StringContainsNul {
160+
err,
161+
s: self.namespace.clone(),
162+
})?;
158163

159164
// SAFETY: No preconditions for this function.
160165
let mut node_handle = unsafe { rcl_get_zero_initialized_node() };

rclrs/src/node/publisher.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,6 @@ where
6363
/// Creates a new `Publisher`.
6464
///
6565
/// Node and namespace changes are always applied _before_ topic remapping.
66-
///
67-
/// # Panics
68-
/// When the topic contains interior null bytes.
6966
pub fn new(node: &Node, topic: &str, qos: QoSProfile) -> Result<Self, RclrsError>
7067
where
7168
T: Message,
@@ -74,7 +71,10 @@ where
7471
let mut publisher_handle = unsafe { rcl_get_zero_initialized_publisher() };
7572
let type_support =
7673
<T as Message>::RmwMsg::get_type_support() as *const rosidl_message_type_support_t;
77-
let topic_c_string = CString::new(topic).unwrap();
74+
let topic_c_string = CString::new(topic).map_err(|err| RclrsError::StringContainsNul {
75+
err,
76+
s: topic.into(),
77+
})?;
7878
let node_handle = &mut *node.handle.lock();
7979

8080
// SAFETY: No preconditions for this function.

rclrs/src/node/subscription.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,6 @@ where
7474
T: Message,
7575
{
7676
/// Creates a new subscription.
77-
///
78-
/// # Panics
79-
/// When the topic contains interior null bytes.
8077
pub fn new<F>(
8178
node: &Node,
8279
topic: &str,
@@ -91,7 +88,10 @@ where
9188
let mut subscription_handle = unsafe { rcl_get_zero_initialized_subscription() };
9289
let type_support =
9390
<T as Message>::RmwMsg::get_type_support() as *const rosidl_message_type_support_t;
94-
let topic_c_string = CString::new(topic).unwrap();
91+
let topic_c_string = CString::new(topic).map_err(|err| RclrsError::StringContainsNul {
92+
err,
93+
s: topic.into(),
94+
})?;
9595
let node_handle = &mut *node.handle.lock();
9696

9797
// SAFETY: No preconditions for this function.

0 commit comments

Comments
 (0)