use std::fmt;
use anyhow::Result;
use crate::internal::ffi::*;
#[derive(Debug)]
pub struct RclErrorBase {
message: String,
file: String,
line: u64,
}
impl RclErrorBase {
unsafe fn new(error_state_ptr: *const rcl_sys::rcutils_error_state_t) -> Self {
let error_state = error_state_ptr.as_ref().unwrap();
Self {
message: String::from_c_char(error_state.message.as_ptr()).unwrap(),
file: String::from_c_char(error_state.file.as_ptr()).unwrap(),
line: error_state.line_number,
}
}
}
impl fmt::Display for RclErrorBase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[rclrust]: {}, at {}:{}",
self.message, self.file, self.line
)
}
}
#[derive(Debug, thiserror::Error)]
pub enum RclRustError {
#[error("Unspecified error.\n{0}")]
RclError(RclErrorBase),
#[error("Timeout occurred.\n{0}")]
RclTimeout(RclErrorBase),
#[error("Failed to allocate memory.\n{0}")]
RclBadAlloc(RclErrorBase),
#[error("Invalid argument.\n{0}")]
RclInvalidArgument(RclErrorBase),
#[error("Unsupported.\n{0}")]
RclUnsupported(RclErrorBase),
#[error("rcl_init() already called.\n{0}")]
RclAlreadyInit(RclErrorBase),
#[error("rcl_init() not yet called.\n{0}")]
RclNotInit(RclErrorBase),
#[error("Mismatched rmw identifier.\n{0}")]
RclMismatchedRmwId(RclErrorBase),
#[error("Topic name does not pass validation.\n{0}")]
RclTopicNameInvalid(RclErrorBase),
#[error("Service name (same as topic name) does not pass validation.\n{0}")]
RclServiceNameInvalid(RclErrorBase),
#[error("Topic name substitution is unknown.\n{0}")]
RclUnknownSubstitution(RclErrorBase),
#[error("rcl_shutdown() already called.\n{0}")]
RclAlreadyShutdown(RclErrorBase),
#[error("Invalid rcl_node_t given.\n{0}")]
RclNodeInvalid(RclErrorBase),
#[error("Invalid node name.\n{0}")]
RclNodeInvalidName(RclErrorBase),
#[error("Invalid node namespace.\n{0}")]
RclNodeInvalidNamespace(RclErrorBase),
#[error("Failed to find node name.\n{0}")]
RclNodeNameNonExistent(RclErrorBase),
#[error("Invalid rcl_publisher_t given.\n{0}")]
RclPublisherInvalid(RclErrorBase),
#[error("Invalid rcl_subscription_t given.\n{0}")]
RclSubscriptionInvalid(RclErrorBase),
#[error("Failed to take a message from the subscription.\n{0}")]
RclSubscriptionTakeFailed(RclErrorBase),
#[error("Invalid rcl_client_t given.\n{0}")]
RclClientInvalid(RclErrorBase),
#[error("Failed to take a response from the client.\n{0}")]
RclClientTakeFailed(RclErrorBase),
#[error("Invalid rcl_service_t given.\n{0}")]
RclServiceInvalid(RclErrorBase),
#[error("Failed to take a request from the service.\n{0}")]
RclServiceTakeFailed(RclErrorBase),
#[error("Invalid rcl_timer_t given.\n{0}")]
RclTimerInvalid(RclErrorBase),
#[error("Given timer was canceled.\n{0}")]
RclTimerCanceled(RclErrorBase),
#[error("Invalid rcl_wait_set_t given.\n{0}")]
RclWaitSetInvalid(RclErrorBase),
#[error("Given rcl_wait_set_t is empty.\n{0}")]
RclWaitSetEmpty(RclErrorBase),
#[error("Given rcl_wait_set_t is full.\n{0}")]
RclWaitSetFull(RclErrorBase),
#[error("Argument is not a valid remap rule.\n{0}")]
RclInvalidRemapRule(RclErrorBase),
#[error("Expected one type of lexeme but got another.\n{0}")]
RclWrongLexeme(RclErrorBase),
#[error("Found invalid ros argument while parsing.\n{0}")]
RclInvalidROSArgs(RclErrorBase),
#[error("Argument is not a valid parameter rule.\n{0}")]
RclInvalidParamRule(RclErrorBase),
#[error("Argument is not a valid log level rule.\n{0}")]
RclInvalidLogLevelRule(RclErrorBase),
#[error("Invalid rcl_event_t given.\n{0}")]
RclEventInvalid(RclErrorBase),
#[error("Failed to take an event from the event handle.\n{0}")]
RclEventTakeFailed(RclErrorBase),
#[error("rcl_lifecycle state registered.\n{0}")]
RclLifecycleStateRegistered(RclErrorBase),
#[error("rcl_lifecycle state not registered.\n{0}")]
RclLifecycleStateNotRegistered(RclErrorBase),
#[error("Runtime Error: {0}")]
RuntimeError(&'static str),
#[error("Service is canceled.")]
ServiceIsCanceled,
#[error(r#"Parameter "{name}" cannot be set because it was not declared."#)]
ParameterNotDeclared { name: String },
#[error(r#"Parameter "{name}" has already been declare."#)]
ParameterAlreadyDeclared { name: String },
#[error(r#"Parameter "{}" could not be set: {reason}"#)]
ParameterInvalidValue { name: String, reason: String },
#[error("{0}")]
ParameterInvalid(String),
#[error("Fail to set parameter: {reason}")]
ParameterSetFail { reason: String },
}
pub(crate) fn result_from_rcl_ret(ret: rcl_sys::rcl_ret_t) -> Result<()> {
if ret as u32 == rcl_sys::RCL_RET_OK {
return Ok(());
}
let error_state = unsafe { rcl_sys::rcutils_get_error_state() };
if error_state.is_null() {
return Err(RclRustError::RuntimeError("rcl error state is not set").into());
}
let base_error = unsafe { RclErrorBase::new(error_state) };
unsafe { rcl_sys::rcutils_reset_error() }
let error = {
use rcl_sys::*;
match ret as u32 {
RCL_RET_TIMEOUT => RclRustError::RclTimeout(base_error),
RCL_RET_BAD_ALLOC => RclRustError::RclBadAlloc(base_error),
RCL_RET_INVALID_ARGUMENT => RclRustError::RclInvalidArgument(base_error),
RCL_RET_UNSUPPORTED => RclRustError::RclUnsupported(base_error),
RCL_RET_ALREADY_INIT => RclRustError::RclAlreadyInit(base_error),
RCL_RET_NOT_INIT => RclRustError::RclNotInit(base_error),
RCL_RET_MISMATCHED_RMW_ID => RclRustError::RclMismatchedRmwId(base_error),
RCL_RET_TOPIC_NAME_INVALID => RclRustError::RclTopicNameInvalid(base_error),
RCL_RET_SERVICE_NAME_INVALID => RclRustError::RclServiceNameInvalid(base_error),
RCL_RET_UNKNOWN_SUBSTITUTION => RclRustError::RclUnknownSubstitution(base_error),
RCL_RET_ALREADY_SHUTDOWN => RclRustError::RclAlreadyShutdown(base_error),
RCL_RET_NODE_INVALID => RclRustError::RclNodeInvalid(base_error),
RCL_RET_NODE_INVALID_NAME => RclRustError::RclNodeInvalidName(base_error),
RCL_RET_NODE_INVALID_NAMESPACE => RclRustError::RclNodeInvalidNamespace(base_error),
RCL_RET_NODE_NAME_NON_EXISTENT => RclRustError::RclNodeNameNonExistent(base_error),
RCL_RET_PUBLISHER_INVALID => RclRustError::RclPublisherInvalid(base_error),
RCL_RET_SUBSCRIPTION_INVALID => RclRustError::RclSubscriptionInvalid(base_error),
RCL_RET_SUBSCRIPTION_TAKE_FAILED => RclRustError::RclSubscriptionTakeFailed(base_error),
RCL_RET_SERVICE_INVALID => RclRustError::RclServiceNameInvalid(base_error),
RCL_RET_SERVICE_TAKE_FAILED => RclRustError::RclServiceTakeFailed(base_error),
RCL_RET_TIMER_INVALID => RclRustError::RclTimerInvalid(base_error),
RCL_RET_TIMER_CANCELED => RclRustError::RclTimerCanceled(base_error),
RCL_RET_WAIT_SET_INVALID => RclRustError::RclWaitSetInvalid(base_error),
RCL_RET_WAIT_SET_EMPTY => RclRustError::RclWaitSetEmpty(base_error),
RCL_RET_WAIT_SET_FULL => RclRustError::RclWaitSetFull(base_error),
RCL_RET_INVALID_REMAP_RULE => RclRustError::RclInvalidRemapRule(base_error),
RCL_RET_WRONG_LEXEME => RclRustError::RclWrongLexeme(base_error),
RCL_RET_INVALID_ROS_ARGS => RclRustError::RclInvalidROSArgs(base_error),
RCL_RET_INVALID_PARAM_RULE => RclRustError::RclInvalidParamRule(base_error),
RCL_RET_INVALID_LOG_LEVEL_RULE => RclRustError::RclInvalidLogLevelRule(base_error),
RCL_RET_EVENT_INVALID => RclRustError::RclEventInvalid(base_error),
RCL_RET_EVENT_TAKE_FAILED => RclRustError::RclEventTakeFailed(base_error),
_ => RclRustError::RclError(base_error),
}
};
Err(error.into())
}
pub(crate) trait ToRclRustResult {
fn to_result(self) -> Result<()>;
}
impl ToRclRustResult for rcl_sys::rcl_ret_t {
fn to_result(self) -> Result<()> {
result_from_rcl_ret(self)
}
}
impl ToRclRustResult for crate::parameter::SetParametersResult {
fn to_result(self) -> Result<()> {
if self.successful {
Ok(())
} else {
Err(RclRustError::ParameterSetFail {
reason: self.reason,
}
.into())
}
}
}