Skip to content

Commit 744aa91

Browse files
authored
Get error string from rcl layer (#164)
1 parent 6c15ef3 commit 744aa91

File tree

7 files changed

+145
-83
lines changed

7 files changed

+145
-83
lines changed

rclrs/src/context.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::rcl_bindings::*;
2-
use crate::{Node, RclReturnCode, ToResult};
2+
use crate::{Node, RclrsError, ToResult};
33

44
use std::ffi::CString;
55
use std::os::raw::c_char;
@@ -58,7 +58,7 @@ impl Context {
5858
///
5959
/// # Panics
6060
/// When there is an interior null byte in any of the args.
61-
pub fn new(args: impl IntoIterator<Item = String>) -> Result<Self, RclReturnCode> {
61+
pub fn new(args: impl IntoIterator<Item = String>) -> Result<Self, RclrsError> {
6262
// SAFETY: Getting a zero-initialized value is always safe
6363
let mut rcl_context = unsafe { rcl_get_zero_initialized_context() };
6464
let cstring_args: Vec<CString> = args
@@ -114,7 +114,7 @@ impl Context {
114114
/// assert!(node.is_ok());
115115
/// # Ok::<(), RclReturnCode>(())
116116
/// ```
117-
pub fn create_node(&self, node_name: &str) -> Result<Node, RclReturnCode> {
117+
pub fn create_node(&self, node_name: &str) -> Result<Node, RclrsError> {
118118
Node::new(node_name, self)
119119
}
120120

@@ -137,7 +137,7 @@ impl Context {
137137
&self,
138138
node_namespace: &str,
139139
node_name: &str,
140-
) -> Result<Node, RclReturnCode> {
140+
) -> Result<Node, RclrsError> {
141141
Node::new_with_namespace(node_namespace, node_name, self)
142142
}
143143

rclrs/src/error.rs

Lines changed: 102 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,50 @@
11
use crate::rcl_bindings::*;
22
use std::error::Error;
3+
use std::ffi::CStr;
34
use std::fmt::{self, Display};
45

6+
/// The main error type.
7+
#[derive(Debug, PartialEq)]
8+
pub struct RclrsError {
9+
/// The error code.
10+
pub code: RclReturnCode,
11+
/// The error message set in the rcl layer or below.
12+
pub(crate) msg: Option<RclErrorMsg>,
13+
}
14+
15+
impl Display for RclrsError {
16+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17+
write!(f, "{}", self.code)
18+
}
19+
}
20+
21+
/// Struct encapsulating an error message from the rcl layer or below.
22+
///
23+
/// This struct is intended to be returned by the `source` method in the implementation of the
24+
/// standard [`Error`][1] trait for [`RclrsError`][2].
25+
/// By doing this, the error message is printed as a separate item in the error chain.
26+
/// This avoids an unreadable, inconsistent formatting of error codes and messages that would
27+
/// likely be produced by a combined display of `RclReturnCode` and message.
28+
///
29+
/// [1]: std::error::Error
30+
/// [2]: crate::RclrsError
31+
#[derive(Debug, PartialEq)]
32+
pub(crate) struct RclErrorMsg(String);
33+
34+
impl Display for RclErrorMsg {
35+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36+
write!(f, "{}", self.0)
37+
}
38+
}
39+
40+
impl Error for RclErrorMsg {}
41+
42+
impl Error for RclrsError {
43+
fn source(&self) -> Option<&(dyn Error + 'static)> {
44+
self.msg.as_ref().map(|e| e as &dyn Error)
45+
}
46+
}
47+
548
/// RCL specific error codes.
649
///
750
/// These are the error codes that start at 100.
@@ -42,19 +85,19 @@ impl TryFrom<i32> for RclErrorCode {
4285
impl Display for RclErrorCode {
4386
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4487
match self {
45-
Self::AlreadyInit => write!(f, "RclError: `rcl_init()` already called!"),
46-
Self::NotInit => write!(f, "RclError: `rcl_init() not yet called!"),
47-
Self::MismatchedRmwId => write!(f, "RclError: Mismatched rmw identifier!"),
88+
Self::AlreadyInit => write!(f, "RclError: `rcl_init()` already called."),
89+
Self::NotInit => write!(f, "RclError: `rcl_init() not yet called."),
90+
Self::MismatchedRmwId => write!(f, "RclError: Mismatched rmw identifier."),
4891
Self::TopicNameInvalid => {
49-
write!(f, "RclError: Topic name does not pass validation!")
92+
write!(f, "RclError: Topic name does not pass validation.")
5093
}
5194
Self::ServiceNameInvalid => {
52-
write!(f, "RclError: Service name does not pass validation!")
95+
write!(f, "RclError: Service name does not pass validation.")
5396
}
5497
Self::UnknownSubstitution => {
55-
write!(f, "RclError: Topic name substitution is unknown!")
98+
write!(f, "RclError: Topic name substitution is unknown.")
5699
}
57-
Self::AlreadyShutdown => write!(f, "RclError: `rcl_shutdown()` already called!"),
100+
Self::AlreadyShutdown => write!(f, "RclError: `rcl_shutdown()` already called."),
58101
}
59102
}
60103
}
@@ -90,10 +133,10 @@ impl TryFrom<i32> for NodeErrorCode {
90133
impl Display for NodeErrorCode {
91134
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92135
match self {
93-
Self::NodeInvalid => write!(f, "NodeError: Invalid `rcl_node_t` given!"),
94-
Self::NodeInvalidName => write!(f, "NodeError: Invalid node name!"),
95-
Self::NodeInvalidNamespace => write!(f, "NodeError: Invalid node namespace!"),
96-
Self::NodeNameNonexistent => write!(f, "NodeError: Failed to find node name!"),
136+
Self::NodeInvalid => write!(f, "NodeError: Invalid `rcl_node_t` given."),
137+
Self::NodeInvalidName => write!(f, "NodeError: Invalid node name."),
138+
Self::NodeInvalidNamespace => write!(f, "NodeError: Invalid node namespace."),
139+
Self::NodeNameNonexistent => write!(f, "NodeError: Failed to find node name."),
97140
}
98141
}
99142
}
@@ -124,11 +167,11 @@ impl Display for SubscriberErrorCode {
124167
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125168
match self {
126169
Self::SubscriptionInvalid => {
127-
write!(f, "SubscriberError: Invalid `rcl_subscription_t` given!")
170+
write!(f, "SubscriberError: Invalid `rcl_subscription_t` given.")
128171
}
129172
Self::SubscriptionTakeFailed => write!(
130173
f,
131-
"SubscriberError: Failed to take a message from the subscription!"
174+
"SubscriberError: Failed to take a message from the subscription."
132175
),
133176
}
134177
}
@@ -158,9 +201,9 @@ impl TryFrom<i32> for ClientErrorCode {
158201
impl Display for ClientErrorCode {
159202
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160203
match self {
161-
Self::ClientInvalid => write!(f, "ClientError: Invalid `rcl_client_t` given!"),
204+
Self::ClientInvalid => write!(f, "ClientError: Invalid `rcl_client_t` given."),
162205
Self::ClientTakeFailed => {
163-
write!(f, "ClientError: Failed to take a response from the client!")
206+
write!(f, "ClientError: Failed to take a response from the client.")
164207
}
165208
}
166209
}
@@ -191,10 +234,10 @@ impl TryFrom<i32> for ServiceErrorCode {
191234
impl Display for ServiceErrorCode {
192235
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193236
match self {
194-
Self::ServiceInvalid => write!(f, "ServiceError: Invalid `rcl_service_t` given!"),
237+
Self::ServiceInvalid => write!(f, "ServiceError: Invalid `rcl_service_t` given."),
195238
Self::ServiceTakeFailed => write!(
196239
f,
197-
"ServiceError: Failed to take a request from the service!"
240+
"ServiceError: Failed to take a request from the service."
198241
),
199242
}
200243
}
@@ -228,8 +271,8 @@ impl TryFrom<i32> for TimerErrorCode {
228271
impl Display for TimerErrorCode {
229272
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230273
match self {
231-
Self::TimerInvalid => write!(f, "TimerError: Invalid `rcl_timer_t` given!"),
232-
Self::TimerCanceled => write!(f, "TimerError: Given timer was canceled!"),
274+
Self::TimerInvalid => write!(f, "TimerError: Invalid `rcl_timer_t` given."),
275+
Self::TimerCanceled => write!(f, "TimerError: Given timer was canceled."),
233276
}
234277
}
235278
}
@@ -262,9 +305,9 @@ impl TryFrom<i32> for WaitSetErrorCode {
262305
impl Display for WaitSetErrorCode {
263306
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264307
match self {
265-
Self::WaitSetInvalid => write!(f, "WaitSetError: Invalid `rcl_wait_set_t` given!"),
266-
Self::WaitSetEmpty => write!(f, "WaitSetError: Given `rcl_wait_set_t` was empty!"),
267-
Self::WaitSetFull => write!(f, "WaitSetError: Given `rcl_wait_set_t` was full!"),
308+
Self::WaitSetInvalid => write!(f, "WaitSetError: Invalid `rcl_wait_set_t` given."),
309+
Self::WaitSetEmpty => write!(f, "WaitSetError: Given `rcl_wait_set_t` was empty."),
310+
Self::WaitSetFull => write!(f, "WaitSetError: Given `rcl_wait_set_t` was full."),
268311
}
269312
}
270313
}
@@ -304,20 +347,20 @@ impl Display for ParsingErrorCode {
304347
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305348
match self {
306349
Self::InvalidRemapRule => {
307-
write!(f, "ParsingError: Argument is not a valid remap rule!")
350+
write!(f, "ParsingError: Argument is not a valid remap rule.")
308351
}
309352
Self::WrongLexeme => write!(
310353
f,
311-
"ParsingError: Expected one type of lexeme, but got another!"
354+
"ParsingError: Expected one type of lexeme, but got another."
312355
),
313356
Self::InvalidRosArgs => {
314-
write!(f, "ParsingError: Found invalid ros argument while parsing!")
357+
write!(f, "ParsingError: Found invalid ROS argument while parsing.")
315358
}
316359
Self::InvalidParamRule => {
317-
write!(f, "ParsingError: Argument is not a valid parameter rule!")
360+
write!(f, "ParsingError: Argument is not a valid parameter rule.")
318361
}
319362
Self::InvalidLogLevelRule => {
320-
write!(f, "ParsingError: Argument is not a valid log level rule!")
363+
write!(f, "ParsingError: Argument is not a valid log level rule.")
321364
}
322365
}
323366
}
@@ -348,10 +391,10 @@ impl TryFrom<i32> for EventErrorCode {
348391
impl Display for EventErrorCode {
349392
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350393
match self {
351-
Self::EventInvalid => write!(f, "EventError: Invalid `rcl_event_t` given!"),
394+
Self::EventInvalid => write!(f, "EventError: Invalid `rcl_event_t` given."),
352395
Self::EventTakeFailed => write!(
353396
f,
354-
"EventError: Failed to take an event from the event handle!"
397+
"EventError: Failed to take an event from the event handle."
355398
),
356399
}
357400
}
@@ -385,10 +428,10 @@ impl Display for LifecycleErrorCode {
385428
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
386429
match self {
387430
Self::LifecycleStateRegistered => {
388-
write!(f, "LifecycleError: `rcl_lifecycle` state registered!")
431+
write!(f, "LifecycleError: `rcl_lifecycle` state registered.")
389432
}
390433
Self::LifecycleStateNotRegistered => {
391-
write!(f, "LifecycleError: `rcl_lifecycle` state not registered!")
434+
write!(f, "LifecycleError: `rcl_lifecycle` state not registered.")
392435
}
393436
}
394437
}
@@ -555,18 +598,18 @@ impl From<LifecycleErrorCode> for RclReturnCode {
555598
impl Display for RclReturnCode {
556599
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
557600
match self {
558-
Self::Ok => write!(f, "RclReturnCode: Operation successful!"),
559-
Self::Error => write!(f, "RclReturnCode: Unspecified error!"),
560-
Self::Timeout => write!(f, "RclReturnCode: Timeout occurred!"),
561-
Self::Unsupported => write!(f, "RclReturnCode: Unsupported return code!"),
562-
Self::BadAlloc => write!(f, "RclReturnCode: Failed to allocate memory!"),
601+
Self::Ok => write!(f, "RclReturnCode: Operation successful."),
602+
Self::Error => write!(f, "RclReturnCode: Unspecified error."),
603+
Self::Timeout => write!(f, "RclReturnCode: Timeout occurred."),
604+
Self::Unsupported => write!(f, "RclReturnCode: Unsupported return code."),
605+
Self::BadAlloc => write!(f, "RclReturnCode: Failed to allocate memory."),
563606
Self::InvalidArgument => {
564-
write!(f, "RclReturnCode: Argument to function was invalid!")
607+
write!(f, "RclReturnCode: Argument to function was invalid.")
565608
}
566609
Self::RclError(rcl_err) => write!(f, "RclReturnCode::{}", rcl_err),
567610
Self::NodeError(node_err) => write!(f, "RclReturnCode::{}", node_err),
568611
Self::PublisherInvalid => {
569-
write!(f, "RclReturnCode: Invalid `rcl_publisher_t` given!")
612+
write!(f, "RclReturnCode: Invalid `rcl_publisher_t` given.")
570613
}
571614
Self::SubscriberError(subscriber_err) => {
572615
write!(f, "RclReturnCode::{}", subscriber_err)
@@ -589,27 +632,40 @@ impl Display for RclReturnCode {
589632

590633
impl Error for RclReturnCode {}
591634

592-
pub(crate) fn to_rcl_result(code: i32) -> Result<(), RclReturnCode> {
635+
pub(crate) fn to_rcl_result(code: i32) -> Result<(), RclrsError> {
593636
match RclReturnCode::from(code) {
594637
RclReturnCode::Ok => Ok(()),
595638
anything_else => {
639+
let mut msg = None;
640+
// SAFETY: No preconditions for this function.
641+
let error_state_ptr = unsafe { rcutils_get_error_state() };
642+
// The returned pointer may be null if the error state has not been set.
643+
if !error_state_ptr.is_null() {
644+
// SAFETY: Dereferencing the pointer is safe since it was checked to be non-null.
645+
let msg_ptr = unsafe { (*error_state_ptr).message.as_ptr() };
646+
// SAFETY: Pointer has been created from an array, no lifetime issues due to
647+
// immediate conversion to owned string.
648+
let s = unsafe { CStr::from_ptr(msg_ptr) }
649+
.to_string_lossy()
650+
.into_owned();
651+
msg = Some(RclErrorMsg(s));
652+
}
596653
// SAFETY: No preconditions for this function.
597654
unsafe { rcutils_reset_error() };
598-
Err(anything_else)
655+
Err(RclrsError {
656+
code: anything_else,
657+
msg,
658+
})
599659
}
600660
}
601661
}
602662

603663
pub(crate) trait ToResult {
604-
fn ok(&self) -> Result<(), RclReturnCode>;
605-
606-
fn unwrap(&self) {
607-
self.ok().unwrap();
608-
}
664+
fn ok(&self) -> Result<(), RclrsError>;
609665
}
610666

611667
impl ToResult for rcl_ret_t {
612-
fn ok(&self) -> Result<(), RclReturnCode> {
668+
fn ok(&self) -> Result<(), RclrsError> {
613669
to_rcl_result(*self as i32)
614670
}
615671
}

rclrs/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use std::time::Duration;
3131
/// This can usually be ignored.
3232
///
3333
/// [1]: crate::SubscriberErrorCode
34-
pub fn spin_once(node: &Node, timeout: Option<Duration>) -> Result<(), RclReturnCode> {
34+
pub fn spin_once(node: &Node, timeout: Option<Duration>) -> Result<(), RclrsError> {
3535
let live_subscriptions = node.live_subscriptions();
3636
let ctx = Context {
3737
handle: node.context.clone(),
@@ -53,7 +53,7 @@ pub fn spin_once(node: &Node, timeout: Option<Duration>) -> Result<(), RclReturn
5353
/// Convenience function for calling [`spin_once`] in a loop.
5454
///
5555
/// This function additionally checks that the context is still valid.
56-
pub fn spin(node: &Node) -> Result<(), RclReturnCode> {
56+
pub fn spin(node: &Node) -> Result<(), RclrsError> {
5757
// The context_is_valid functions exists only to abstract away ROS distro differences
5858
#[cfg(ros_distro = "foxy")]
5959
// SAFETY: No preconditions for this function.
@@ -64,9 +64,9 @@ pub fn spin(node: &Node) -> Result<(), RclReturnCode> {
6464

6565
while context_is_valid() {
6666
if let Some(error) = spin_once(node, None).err() {
67-
match error {
67+
match error.code {
6868
RclReturnCode::Timeout => continue,
69-
error => return Err(error),
69+
_ => return Err(error),
7070
};
7171
}
7272
}

0 commit comments

Comments
 (0)