use std::error::{self, Error as _};
use std::fmt::{self, Display};
use std::panic::Location;
use std::borrow::Cow;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
#[repr(transparent)]
pub struct Error {
inner: Box<ErrorInner>,
}
#[derive(Debug)]
struct ErrorInner {
reason: Cow<'static, str>,
description: Cow<'static, str>,
origin: Cow<'static, str>,
severity: ErrorSeverity,
kind: Option<ErrorKind<'static>>,
source: Option<ErrorSource>,
}
#[derive(Debug)]
enum ErrorSource {
Parent(Error),
Generic(Box<dyn error::Error + Send + Sync>),
}
impl Error {
#[track_caller]
pub fn new(kind: ErrorKind<'_>) -> Self {
let mut ret = Self {
inner: Box::new(ErrorInner {
reason: Cow::Borrowed(""),
description: Cow::Borrowed(""),
origin: Cow::Borrowed(""),
severity: ErrorSeverity::Error,
kind: None,
source: None,
})
};
ret.set_kind(kind);
ret.set_origin_from_caller();
ret
}
pub fn reason(&self) -> &str {
&self.inner.reason
}
pub fn kind(&self) -> ErrorKind<'_> {
if let Some(kind) = self.inner.kind {
kind
} else {
ErrorKind::Reason(&self.inner.reason)
}
}
pub fn set_kind(&mut self, kind: ErrorKind<'_>) {
let (reason, kind) = match kind.into_static() {
Ok(kind) => (Cow::Borrowed(kind.reason()), Some(kind)),
Err(kind) => (Cow::Owned(kind.reason().to_string()), None),
};
self.inner.kind = kind;
self.inner.reason = reason;
}
pub fn with_kind(mut self, kind: ErrorKind<'_>) -> Self {
self.set_kind(kind);
self
}
pub fn description(&self) -> &str {
&self.inner.description
}
pub fn append_description(&mut self, additional: impl Display) {
if self.inner.description.is_empty() {
self.inner.description = Cow::Owned(additional.to_string());
} else {
self.inner.description = Cow::Owned(format!("{}\n{additional}", self.inner.description));
}
}
pub fn set_description(&mut self, description: impl Into<Cow<'static, str>>) {
self.inner.description = description.into();
}
pub fn with_append_description(mut self, additional: impl Display) -> Self {
self.append_description(additional);
self
}
pub fn with_description(mut self, description: impl Into<Cow<'static, str>>) -> Self {
self.set_description(description);
self
}
pub fn severity(&self) -> ErrorSeverity {
self.inner.severity
}
pub fn set_severity(&mut self, severity: ErrorSeverity) {
self.inner.severity = severity;
}
pub fn with_severity(mut self, severity: ErrorSeverity) -> Self {
self.set_severity(severity);
self
}
pub fn origin(&self) -> &str {
&self.inner.origin
}
pub fn append_origin(&mut self, additional: impl Display) {
if self.inner.origin.is_empty() {
self.inner.origin = Cow::Owned(additional.to_string());
} else {
self.inner.origin = Cow::Owned(format!("{} {additional}", self.inner.origin));
}
}
pub fn set_origin(&mut self, origin: impl Into<Cow<'static, str>>) {
self.inner.origin = origin.into();
}
pub fn with_append_origin(mut self, additional: impl Display) -> Self {
self.append_origin(additional);
self
}
pub fn with_origin(mut self, origin: impl Into<Cow<'static, str>>) -> Self {
self.set_origin(origin);
self
}
#[track_caller]
pub fn set_origin_from_caller(&mut self) {
let location = Location::caller();
self.set_origin(format!("{location}"));
}
#[track_caller]
pub fn with_origin_from_caller(mut self) -> Self {
self.set_origin_from_caller();
self
}
pub fn parent(&self) -> Option<&Error> {
if let Some(ErrorSource::Parent(err)) = &self.inner.source {
Some(err)
} else {
None
}
}
pub fn set_parent(&mut self, parent: Error) {
self.inner.source = Some(ErrorSource::Parent(parent));
}
pub fn with_parent(mut self, parent: Error) -> Self {
self.set_parent(parent);
self
}
pub fn set_source(&mut self, source: Box<dyn error::Error + Send + Sync>) {
self.inner.source = Some(ErrorSource::Generic(source));
}
pub fn with_source(mut self, source: Box<dyn error::Error + Send + Sync>) -> Self {
self.set_source(source);
self
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.inner.source {
Some(ErrorSource::Parent(err)) => Some(err),
Some(ErrorSource::Generic(err)) => Some(&**err),
None => None,
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let severity = match self.severity() {
ErrorSeverity::Warning => "WARNING",
ErrorSeverity::Error => "ERROR",
ErrorSeverity::Panic => "PANIC",
};
write!(f, "{severity} {}", self.reason())?;
for line in self.description().lines() {
write!(f, "\n {line}")?;
}
if let Some(parent) = self.parent() {
write!(f, "\n{parent}")?;
} else if let Some(source) = self.source() {
write!(f, "\nRAW {source}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorKind<'r> {
NotHandled,
DatabaseNotConnected,
DatabaseNeeded,
AliasNotFound,
DevNotFound,
ServerNotConnected,
DevNotConnected,
CommandNameInvalid,
CommandNotFound,
CommandArgumentKindInvalid,
AttrNameInvalid,
AttrNotFound,
AttrNotWritable,
AttrKindInvalid,
EventNotConnected,
InvalidIndex,
InvalidData,
TimedOut,
Internal,
Reason(&'r str),
}
impl<'r> ErrorKind<'r> {
fn into_static(self) -> std::result::Result<ErrorKind<'static>, Self> {
Ok(match self {
ErrorKind::NotHandled => ErrorKind::NotHandled,
ErrorKind::DatabaseNotConnected => ErrorKind::DatabaseNotConnected,
ErrorKind::DatabaseNeeded => ErrorKind::DatabaseNeeded,
ErrorKind::AliasNotFound => ErrorKind::AliasNotFound,
ErrorKind::DevNotFound => ErrorKind::DevNotFound,
ErrorKind::ServerNotConnected => ErrorKind::ServerNotConnected,
ErrorKind::DevNotConnected => ErrorKind::DevNotConnected,
ErrorKind::CommandNameInvalid => ErrorKind::CommandNameInvalid,
ErrorKind::CommandNotFound => ErrorKind::CommandNotFound,
ErrorKind::CommandArgumentKindInvalid => ErrorKind::CommandArgumentKindInvalid,
ErrorKind::AttrNameInvalid => ErrorKind::AttrNameInvalid,
ErrorKind::AttrNotFound => ErrorKind::AttrNotFound,
ErrorKind::AttrNotWritable => ErrorKind::AttrNotWritable,
ErrorKind::AttrKindInvalid => ErrorKind::AttrKindInvalid,
ErrorKind::EventNotConnected => ErrorKind::EventNotConnected,
ErrorKind::InvalidIndex => ErrorKind::InvalidIndex,
ErrorKind::InvalidData => ErrorKind::InvalidData,
ErrorKind::TimedOut => ErrorKind::TimedOut,
ErrorKind::Internal => ErrorKind::Internal,
ErrorKind::Reason(_) => return Err(self),
})
}
pub fn reason(&self) -> &'r str {
match self {
ErrorKind::NotHandled => reason::API_InternalError,
ErrorKind::DatabaseNotConnected => reason::API_CantConnectToDatabase,
ErrorKind::DatabaseNeeded => reason::API_InternalError,
ErrorKind::AliasNotFound => reason::API_AliasNotDefined,
ErrorKind::DevNotFound => reason::API_DeviceNotExported,
ErrorKind::ServerNotConnected => reason::API_ServerNotRunning,
ErrorKind::DevNotConnected => reason::API_DeviceNotDefined,
ErrorKind::CommandNameInvalid => reason::API_InternalError,
ErrorKind::CommandNotFound => reason::API_CommandNotFound,
ErrorKind::CommandArgumentKindInvalid => reason::API_IncompatibleCmdArgumentType,
ErrorKind::AttrNameInvalid => reason::API_InternalError,
ErrorKind::AttrNotFound => reason::API_AttrNotFound,
ErrorKind::AttrNotWritable => reason::API_AttrNotWritable,
ErrorKind::AttrKindInvalid => reason::API_IncompatibleAttrDataType,
ErrorKind::EventNotConnected => reason::API_EventNotFound,
ErrorKind::InvalidIndex => reason::API_InternalError,
ErrorKind::InvalidData => reason::API_InternalError,
ErrorKind::TimedOut => reason::API_DeviceTimedOut,
ErrorKind::Internal => reason::API_InternalError,
ErrorKind::Reason(reason) => reason,
}
}
pub fn description(&self) -> &'static str {
match self {
ErrorKind::NotHandled => "No backend is able to handle such request",
ErrorKind::DatabaseNotConnected => "Not able to connect to any database in order to find the device",
ErrorKind::DatabaseNeeded => "This operation is only allowed if the device is connected through a database",
ErrorKind::AliasNotFound => "Alias was not found in the database",
ErrorKind::DevNotFound => "Device was not found in the database",
ErrorKind::ServerNotConnected => "Device server hosting the device is not connected",
ErrorKind::DevNotConnected => "Device was not found on its device server",
ErrorKind::CommandNameInvalid => "Command name may contain invalid characters or is too long",
ErrorKind::CommandNotFound => "The command was not found on the device",
ErrorKind::CommandArgumentKindInvalid => "Invalid command argument data kind",
ErrorKind::AttrNameInvalid => "Attribute name may contain invalid characters or is too long",
ErrorKind::AttrNotFound => "The attribute was not found on the device",
ErrorKind::AttrNotWritable => "The attribute is not writable",
ErrorKind::AttrKindInvalid => "Invalid attribute data kind",
ErrorKind::EventNotConnected => "Not able to connect to an event for subscribing",
ErrorKind::InvalidIndex => "Invalid index for data",
ErrorKind::InvalidData => "Failed to handle data",
ErrorKind::TimedOut => "The network operation timed out",
ErrorKind::Internal => "",
ErrorKind::Reason(_) => "",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ErrorSeverity {
Warning,
Error,
Panic,
}
#[allow(non_upper_case_globals)]
pub mod reason {
macro_rules! define_reasons {
( $($reason:ident),* $(,)? ) => {
$( pub const $reason: &str = stringify!($reason); )*
};
}
define_reasons![
API_AliasNotDefined,
API_AlreadyPolled,
API_AsynReplyNotArrived,
API_AttrConfig,
API_AttrEventProp,
API_AttributeFailed,
API_AttributeNotDataReadyEnabled,
API_AttributePollingNotStarted,
API_AttrIncorrectDataNumber,
API_AttrNoAlarm,
API_AttrNotAllowed,
API_AttrNotFound,
API_AttrNotPolled,
API_AttrNotWritable,
API_AttrOptProp,
API_AttrPropValueNotSet,
API_AttrValueNotSet,
API_AttrWrongDefined,
API_AttrWrongMemValue,
API_BadAsyn,
API_BadAsynPollId,
API_BadAsynReqType,
API_BadConfigurationProperty,
API_BlackBoxArgument,
API_BlackBoxEmpty,
API_CannotCheckAccessControl,
API_CannotOpenFile,
API_CantActivatePOAManager,
API_CantConnectToDatabase,
API_CantConnectToDevice,
API_CantCreateClassPoa,
API_CantCreateLockingThread,
API_CantDestroyDevice,
API_CantFindLockingThread,
API_CantGetClientIdent,
API_CantGetDevObjectId,
API_CantInstallSignal,
API_CantRetrieveClass,
API_CantRetrieveClassList,
API_CantStoreDeviceClass,
API_ClassNotFound,
API_ClientTooOld,
API_CmdArgumentTypeNotSupported,
API_CmdNotPolled,
API_CommandFailed,
API_CommandNotAllowed,
API_CommandNotFound,
API_CommandTimedOut,
API_CommunicationFailed,
API_ConnectionFailed,
API_CorbaException,
API_CorbaSysException,
API_CorruptedDatabase,
API_DatabaseAccess,
API_DatabaseCacheAccess,
API_DatabaseFileError,
API_DecodeErr,
API_EncodeErr,
API_DeprecatedCommand,
API_DeviceLocked,
API_DeviceNotDefined,
API_DeviceNotExported,
API_DeviceNotFound,
API_DeviceNotLocked,
API_DeviceNotPolled,
API_DeviceTimedOut,
API_DeviceUnlockable,
API_DeviceUnlocked,
API_DServerClassNotInitialised,
API_DSFailedRegisteringEvent,
API_DummyException,
API_EmptyDataElement,
API_EmptyDbDatum,
API_EmptyDbDbDatum,
API_EmptyDeviceAttribute,
API_EmptyDeviceData,
API_EventChannelNotExported,
API_EventConsumer,
API_EventNotFound,
API_EventPropertiesNotSet,
API_EventQueues,
API_EventSupplierNotConstructed,
API_EventTimeout,
API_FwdAttrInconsistency,
API_FwdAttrNotConfigured,
API_HistoryInvalid,
API_IncoherentDbData,
API_IncoherentDevData,
API_IncoherentValues,
API_IncompatibleArgumentType,
API_IncompatibleAttrArgumentType,
API_IncompatibleAttrDataType,
API_IncompatibleCmdArgumentType,
API_InitMethodNotFound,
API_InitNotPublic,
API_InitThrowsException,
API_InternalError,
API_InvalidArgs,
API_InvalidCorbaAny,
API_JavaRuntimeSecurityException,
API_MemAttFailedDuringInit,
API_MemoryAllocation,
API_MethodArgument,
API_MethodNotFound,
API_MissedEvents,
API_NoDataYet,
API_NonDatabaseDevice,
API_NoSetValueAvailable,
API_NotificationServiceFailed,
API_NotSupported,
API_NotSupportedFeature,
API_NotUpdatedAnyMore,
API_NtDebugWindowError,
API_OverloadingNotSupported,
API_PipeDataEltNotFound,
API_PipeDuplicateDEName,
API_PipeFailed,
API_PipeNoDataElement,
API_PipeNotAllowed,
API_PipeNotFound,
API_PipeNotWritable,
API_PipeOptProp,
API_PipeValueNotSet,
API_PipeWrongArg,
API_PipeWrongArgNumber,
API_PolledDeviceNotInPoolConf,
API_PolledDeviceNotInPoolMap,
API_PollingThreadNotFound,
API_PollObjNotFound,
API_PollRingBufferEmpty,
API_PollThreadOutOfSync,
API_ReadOnlyMode,
API_RootAttrFailed,
API_ServerNotRunning,
API_ShutdownInProgress,
API_SignalOutOfRange,
API_StartupSequence,
API_StdException,
API_SystemCallFailed,
API_TangoHostNotSet,
API_ThrowException,
API_UnsupportedAttribute,
API_UnsupportedDBaseModifier,
API_UnsupportedFeature,
API_UnsupportedProtocol,
API_UtilSingletonNotCreated,
API_UtilSingletonPoisoned,
API_WAttrOutsideLimit,
API_WizardConfError,
API_WrongAttributeNameSyntax,
API_WrongCmdLineArgs,
API_WrongDeviceNameSyntax,
API_WrongEventData,
API_WrongFormat,
API_WrongHistoryDataBuffer,
API_WrongLockingStatus,
API_WrongNumberOfArgs,
API_WrongWildcardUsage,
API_ZmqFailed,
API_ZmqInitFailed,
DB_AliasNotDefined,
DB_ClassNotFoundInCache,
DB_DeviceNotDefined,
DB_DeviceNotFoundInCache,
DB_SQLError,
DB_TooOldStoredProc,
];
}