use std::fmt::{Debug, Formatter};
use std::sync::{mpsc, Arc, PoisonError};
use crate::protocol::MessageId;
#[doc(inline)]
pub use crate::error::Error as CoreError;
#[doc(inline)]
#[cfg(all(feature = "msrv-utils-mission", feature = "unstable"))]
pub use mavio::error::MissionError;
#[doc(inline)]
pub use mavio::error::{
ChecksumError, FrameError, IncompatFlagsError, IoError, SignatureError, SpecError, VersionError,
};
pub type Result<T> = core::result::Result<T, Error>;
pub type SendResult<T> = core::result::Result<(), SendError<T>>;
pub type RecvResult<T> = core::result::Result<T, RecvError>;
pub type RecvTimeoutResult<T> = core::result::Result<T, RecvTimeoutError>;
pub type TryRecvResult<T> = core::result::Result<T, TryRecvError>;
#[cfg_attr(all(feature = "specta", feature = "unstable"), derive(specta::Type))]
#[cfg_attr(
all(feature = "specta", feature = "unstable"),
specta(rename = "MaviolaError")
)]
#[cfg_attr(
all(feature = "serde", feature = "unstable"),
derive(serde::Serialize, serde::Deserialize)
)]
#[derive(Clone, Debug, thiserror::Error)]
pub enum Error {
#[error("I/O error: {0:?}")]
Io(IoError),
#[error("frame decoding/encoding error: {0:?}")]
Frame(#[from] FrameError),
#[error("message decoding/encoding error: {0:?}")]
Spec(SpecError),
#[error("node error: {0:?}")]
#[cfg_attr(
all(feature = "serde", feature = "unstable"),
serde(skip_deserializing)
)]
Node(#[from] NodeError),
#[error("multi-threading error: {0:?}")]
Sync(#[from] SyncError),
#[error("serial port error: {0:?}")]
Serial(#[from] SerialError),
#[cfg(all(feature = "msrv-utils-mission", feature = "unstable"))]
#[error("mission error: {0:?}")]
Mission(#[from] MissionError),
#[error("error: {0}")]
Other(String),
}
#[cfg_attr(all(feature = "specta", feature = "unstable"), derive(specta::Type))]
#[cfg_attr(
all(feature = "serde", feature = "unstable"),
derive(serde::Serialize, serde::Deserialize)
)]
#[derive(Clone, Debug, thiserror::Error)]
pub enum SyncError {
#[error("error during thread join: {0:?}")]
ThreadJoin(String),
#[error("poisoned mutex: {0}")]
PoisonedMutex(String),
#[error("channel is empty")]
Empty,
#[error("channel is closed")]
Disconnected,
#[error("receiver is too far behind: {0}")]
Lagged(u64),
#[error("timed out")]
Timeout,
}
#[derive(Clone, Debug, thiserror::Error)]
#[cfg_attr(all(feature = "specta", feature = "unstable"), derive(specta::Type))]
#[cfg_attr(
all(feature = "serde", feature = "unstable"),
derive(serde::Serialize, serde::Deserialize)
)]
pub enum NodeError {
#[error("transport is no longer active")]
Inactive,
#[error("provided frame with ID = {0} can't be decoded in current dialect {1}")]
NotInDialect(MessageId, &'static str),
}
#[derive(Clone, Debug, thiserror::Error)]
#[error("{inner:?}")]
pub struct SerialError {
#[cfg(feature = "sync")]
#[cfg(any(windows, unix))]
inner: Arc<serialport::Error>,
#[cfg(all(feature = "async", not(feature = "sync")))]
inner: Arc<tokio_serial::Error>,
#[cfg(not(any(feature = "async", feature = "sync")))]
inner: (),
#[cfg(feature = "sync")]
#[cfg(not(any(windows, unix)))]
inner: (),
}
pub struct SendError<T>(pub T);
#[derive(Clone, Copy, Debug, thiserror::Error)]
pub enum RecvError {
#[error("channel is disconnected")]
Disconnected,
#[error("lagged: {0}")]
Lagged(u64),
}
#[derive(Clone, Copy, Debug, thiserror::Error)]
pub enum RecvTimeoutError {
#[error("channel is disconnected")]
Disconnected,
#[error("timed out")]
Timeout,
#[error("lagged: {0}")]
Lagged(u64),
}
#[derive(Clone, Copy, Debug, thiserror::Error)]
pub enum TryRecvError {
#[error("channel is empty")]
Empty,
#[error("channel is disconnected")]
Disconnected,
#[error("lagged: {0}")]
Lagged(u64),
}
impl From<mavio::error::Error> for Error {
fn from(value: mavio::error::Error) -> Self {
match value {
mavio::error::Error::Io(err) => Self::Io(err),
mavio::error::Error::Frame(err) => Self::Frame(err),
mavio::error::Error::Spec(err) => Self::Spec(err),
#[cfg(all(feature = "msrv-utils-mission", feature = "unstable"))]
mavio::error::Error::Mission(err) => Self::Mission(err),
}
}
}
impl From<SpecError> for Error {
fn from(value: SpecError) -> Self {
Error::Spec(value)
}
}
impl<Guard> From<PoisonError<Guard>> for Error {
fn from(value: PoisonError<Guard>) -> Self {
Error::Sync(SyncError::PoisonedMutex(format!("{:?}", value)))
}
}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Self::Io(IoError::from(value))
}
}
impl From<VersionError> for Error {
fn from(value: VersionError) -> Self {
FrameError::from(value).into()
}
}
impl From<ChecksumError> for Error {
fn from(value: ChecksumError) -> Self {
FrameError::from(value).into()
}
}
impl From<SignatureError> for Error {
fn from(value: SignatureError) -> Self {
FrameError::from(value).into()
}
}
impl From<IncompatFlagsError> for Error {
fn from(value: IncompatFlagsError) -> Self {
FrameError::from(value).into()
}
}
impl<T> Debug for SendError<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SendError").finish_non_exhaustive()
}
}
impl<T> From<SendError<T>> for Error {
fn from(_: SendError<T>) -> Self {
SyncError::Disconnected.into()
}
}
impl From<RecvError> for Error {
fn from(value: RecvError) -> Self {
match value {
RecvError::Disconnected => SyncError::Disconnected,
RecvError::Lagged(n) => SyncError::Lagged(n),
}
.into()
}
}
impl From<RecvTimeoutError> for Error {
fn from(value: RecvTimeoutError) -> Self {
match value {
RecvTimeoutError::Disconnected => SyncError::Disconnected,
RecvTimeoutError::Timeout => SyncError::Timeout,
RecvTimeoutError::Lagged(n) => SyncError::Lagged(n),
}
.into()
}
}
impl From<TryRecvError> for Error {
fn from(value: TryRecvError) -> Self {
match value {
TryRecvError::Empty => SyncError::Empty,
TryRecvError::Disconnected => SyncError::Disconnected,
TryRecvError::Lagged(n) => SyncError::Lagged(n),
}
.into()
}
}
impl<T> From<mpsc::SendError<T>> for SendError<T> {
fn from(value: mpsc::SendError<T>) -> Self {
SendError(value.0)
}
}
impl From<mpsc::RecvError> for RecvError {
fn from(_: mpsc::RecvError) -> Self {
RecvError::Disconnected
}
}
impl From<mpsc::RecvTimeoutError> for RecvTimeoutError {
fn from(value: mpsc::RecvTimeoutError) -> Self {
match value {
mpsc::RecvTimeoutError::Timeout => RecvTimeoutError::Timeout,
mpsc::RecvTimeoutError::Disconnected => RecvTimeoutError::Disconnected,
}
}
}
impl From<mpsc::TryRecvError> for TryRecvError {
fn from(value: mpsc::TryRecvError) -> Self {
match value {
mpsc::TryRecvError::Empty => TryRecvError::Empty,
mpsc::TryRecvError::Disconnected => TryRecvError::Disconnected,
}
}
}
impl<T> From<mpsc::SendError<T>> for Error {
fn from(_: mpsc::SendError<T>) -> Self {
SyncError::Disconnected.into()
}
}
impl From<mpsc::RecvError> for Error {
fn from(_: mpsc::RecvError) -> Self {
SyncError::Disconnected.into()
}
}
impl From<mpsc::RecvTimeoutError> for Error {
fn from(value: mpsc::RecvTimeoutError) -> Self {
RecvTimeoutError::from(value).into()
}
}
impl From<mpsc::TryRecvError> for Error {
fn from(value: mpsc::TryRecvError) -> Self {
TryRecvError::from(value).into()
}
}
#[cfg(feature = "async")]
impl<T> From<tokio::sync::broadcast::error::SendError<T>> for SendError<T> {
fn from(value: tokio::sync::broadcast::error::SendError<T>) -> Self {
Self(value.0)
}
}
#[cfg(feature = "async")]
impl From<tokio::sync::broadcast::error::RecvError> for RecvError {
fn from(value: tokio::sync::broadcast::error::RecvError) -> Self {
match value {
tokio::sync::broadcast::error::RecvError::Closed => RecvError::Disconnected,
tokio::sync::broadcast::error::RecvError::Lagged(val) => RecvError::Lagged(val),
}
}
}
#[cfg(feature = "async")]
impl From<tokio::sync::broadcast::error::TryRecvError> for TryRecvError {
fn from(value: tokio::sync::broadcast::error::TryRecvError) -> Self {
match value {
tokio::sync::broadcast::error::TryRecvError::Empty => TryRecvError::Empty,
tokio::sync::broadcast::error::TryRecvError::Closed => TryRecvError::Disconnected,
tokio::sync::broadcast::error::TryRecvError::Lagged(val) => TryRecvError::Lagged(val),
}
}
}
#[cfg(feature = "async")]
impl<T> From<tokio::sync::broadcast::error::SendError<T>> for Error {
fn from(_: tokio::sync::broadcast::error::SendError<T>) -> Self {
SyncError::Disconnected.into()
}
}
#[cfg(feature = "async")]
impl From<tokio::sync::broadcast::error::RecvError> for Error {
fn from(value: tokio::sync::broadcast::error::RecvError) -> Self {
RecvError::from(value).into()
}
}
#[cfg(feature = "async")]
impl From<tokio::sync::broadcast::error::TryRecvError> for Error {
fn from(value: tokio::sync::broadcast::error::TryRecvError) -> Self {
TryRecvError::from(value).into()
}
}
#[cfg(feature = "async")]
impl<T> From<tokio::sync::mpsc::error::SendError<T>> for SendError<T> {
fn from(value: tokio::sync::mpsc::error::SendError<T>) -> Self {
SendError(value.0)
}
}
#[cfg(feature = "async")]
impl<T> From<tokio::sync::mpsc::error::SendError<T>> for Error {
fn from(value: tokio::sync::mpsc::error::SendError<T>) -> Self {
SendError::from(value).into()
}
}
#[cfg(feature = "sync")]
#[cfg(any(windows, unix))]
impl AsRef<serialport::Error> for SerialError {
fn as_ref(&self) -> &serialport::Error {
self.inner.as_ref()
}
}
#[cfg(feature = "sync")]
#[cfg(any(windows, unix))]
impl From<serialport::Error> for SerialError {
fn from(value: serialport::Error) -> Self {
SerialError {
inner: Arc::new(value),
}
}
}
#[cfg(all(feature = "async", not(feature = "sync")))]
impl AsRef<tokio_serial::Error> for SerialError {
fn as_ref(&self) -> &tokio_serial::Error {
self.inner.as_ref()
}
}
#[cfg(all(feature = "async", not(feature = "sync")))]
impl From<tokio_serial::Error> for SerialError {
fn from(value: tokio_serial::Error) -> Self {
SerialError {
inner: Arc::new(value),
}
}
}
#[cfg(feature = "sync")]
#[cfg(any(windows, unix))]
impl From<serialport::Error> for Error {
fn from(value: serialport::Error) -> Self {
match value.kind {
serialport::ErrorKind::Io(kind) => {
Self::Io(IoError::from(std::io::Error::new(kind, value.description)))
}
_ => Self::Serial(SerialError::from(value)),
}
}
}
#[cfg(all(feature = "async", not(feature = "sync")))]
impl From<tokio_serial::Error> for Error {
fn from(value: tokio_serial::Error) -> Self {
match value.kind {
tokio_serial::ErrorKind::Io(kind) => {
Self::Io(IoError::from(std::io::Error::new(kind, value.description)))
}
_ => Self::Serial(SerialError::from(value)),
}
}
}
#[cfg(all(feature = "serde", feature = "unstable"))]
impl serde::Serialize for SerialError {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut io_error = serializer.serialize_struct("SerialError", 2)?;
#[cfg(feature = "sync")]
#[cfg(any(windows, unix))]
{
io_error.serialize_field("kind", &format!("{:?}", self.as_ref().kind()))?;
io_error.serialize_field("error", &self.as_ref().to_string())?;
}
#[cfg(all(feature = "async", not(feature = "sync")))]
{
io_error.serialize_field("kind", &format!("{:?}", self.as_ref().kind()))?;
io_error.serialize_field("error", &self.as_ref().to_string())?;
}
#[cfg(not(all(feature = "async", feature = "sync")))]
{
io_error.serialize_field("kind", "Unknown")?;
io_error.serialize_field("error", "Unknown")?;
}
#[cfg(feature = "sync")]
#[cfg(not(any(windows, unix)))]
{
io_error.serialize_field("kind", "Unknown")?;
io_error.serialize_field("error", "Unknown")?;
}
io_error.end()
}
}
#[cfg(all(feature = "serde", feature = "unstable"))]
impl<'de> serde::Deserialize<'de> for SerialError {
fn deserialize<D>(_: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[cfg(feature = "sync")]
#[cfg(any(windows, unix))]
return Ok(SerialError::from(serialport::Error::new(
serialport::ErrorKind::Unknown,
"Foreign error",
)));
#[cfg(all(feature = "async", not(feature = "sync")))]
return Ok(SerialError::from(tokio_serial::Error::new(
tokio_serial::ErrorKind::Unknown,
"Foreign error",
)));
#[cfg(not(any(feature = "async", feature = "sync")))]
return Ok(SerialError { inner: () });
#[cfg(feature = "sync")]
#[cfg(not(any(windows, unix)))]
return Ok(SerialError { inner: () });
}
}
#[cfg(all(feature = "specta", feature = "unstable"))]
#[derive(specta::Type)]
#[allow(dead_code)]
struct SerialErrorStub {
kind: String,
error: String,
}
#[cfg(all(feature = "specta", feature = "unstable"))]
impl specta::Type for SerialError {
fn inline(type_map: &mut specta::TypeMap, generics: specta::Generics) -> specta::DataType {
specta::DataType::from(SerialErrorStub::inline(type_map, generics))
}
}