1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use mio_extras::channel::TrySendError;

/// This is a specialized Result, similar to std::io::Result
pub type Result<T> = std::result::Result<T, Error>;

/// This roughly corresponds to "Return codes" in DDS spec 2.2.1.1 Format and
/// Conventions
///
/// Deviations from the DDS spec:
/// * `OK` is not included. It is not an error. Ok/Error should be distinguished
///   with the `Result` type.
/// * `Error` is too unspecific.
/// * `AlreadyDeleted` We should use Rust type system to avoid these, so no need
///   for run-time error.
/// * `Timeout`  This is normal operation and should be encoded as `Option` or
///   `Result`
/// * `NoData`  This should be encoded as `Option<SomeData>`, not an error code.
#[derive(Debug, thiserror::Error)]
pub enum Error {
  /// Illegal parameter value.
  #[error("Bad parameter: {reason}")]
  BadParameter { reason: String },

  /// Unsupported operation. Can only be returned by operations that are
  /// optional.
  #[error("Unsupported operation")]
  Unsupported,

  /// Service ran out of the resources needed to complete the operation.
  #[error("Out of resources")]
  OutOfResources,

  /// Operation invoked on an Entity that is not yet enabled.
  #[error("Entity not yet enabled")]
  NotEnabled,

  /// Application attempted to modify an immutable QosPolicy.
  #[error("Attempted to modify immutable entity")]
  ImmutablePolicy, // can we check this statically?

  /// Application specified a set of policies that are not consistent with each
  /// other.
  #[error("Inconsistent policies: {reason}")]
  InconsistentPolicy { reason: String },

  /// A pre-condition for the operation was not met.
  #[error("Precondition not met: {precondition}")]
  PreconditionNotMet { precondition: String },

  /// An operation was invoked on an inappropriate object or at
  /// an inappropriate time (as determined by policies set by the
  /// specification or the Service implementation). There is no
  /// precondition that could be changed to make the operation
  /// succeed.
  #[error("Illegal operation: {reason}")]
  IllegalOperation { reason: String },

  // Our own additions to the DDS spec below:
  /// Synchronization with another thread failed because the [other thread
  /// has exited while holding a lock.](https://doc.rust-lang.org/std/sync/struct.PoisonError.html)
  /// Does not exist in the DDS spec.
  #[error("Lock poisoned")]
  LockPoisoned,

  /// Something that should not go wrong went wrong anyway.
  /// This is usually a bug in RustDDS
  #[error("Internal error: {reason}")]
  Internal { reason: String },

  #[error(transparent)]
  Io(#[from] std::io::Error),

  #[error("Serialization error: {reason}")]
  Serialization { reason: String },

  #[error("Discovery error: {reason}")]
  Discovery { reason: String },
}

impl Error {
  pub fn bad_parameter<T>(reason: impl Into<String>) -> Result<T> {
    Err(Error::BadParameter {
      reason: reason.into(),
    })
  }

  pub fn precondition_not_met<T>(precondition: impl Into<String>) -> Result<T> {
    Err(Error::PreconditionNotMet {
      precondition: precondition.into(),
    })
  }
}

#[doc(hidden)]
#[macro_export]
macro_rules! log_and_err_precondition_not_met {
  ($err_msg:literal) => {{
    log::error!($err_msg);
    Error::precondition_not_met($err_msg)
  }};
}

#[doc(hidden)]
#[macro_export]
macro_rules! log_and_err_internal {
  ($($arg:tt)*) => (
      { log::error!($($arg)*);
        Err( Error::Internal{ reason: format!($($arg)*) } )
      }
    )
}

#[doc(hidden)]
#[macro_export]
macro_rules! log_and_err_discovery {
  ($($arg:tt)*) => (
      { error!($($arg)*);
        Error::Message(format!($($arg)*) )
      }
    )
}

impl<T> From<std::sync::PoisonError<T>> for Error {
  fn from(_e: std::sync::PoisonError<T>) -> Error {
    Error::LockPoisoned
  }
}

impl<T> From<TrySendError<T>> for Error
where
  TrySendError<T>: std::error::Error,
{
  fn from(e: TrySendError<T>) -> Error {
    Error::Internal {
      reason: format!("Cannot send to internal mio channel: {:?}", e),
    }
  }
}