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 std;
use std::result;

#[allow(unused_imports)]
use log::{debug, info, warn, trace, error};

use mio_extras::channel::{TrySendError};

/// This is a specialized Result, similar to std::io::Result
pub type Result<T> = 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)]
pub enum Error {
  /// Illegal parameter value.
  BadParameter {
    reason: String,
  },
  /// Unsupported operation. Can only be returned by operations that are optional.
  Unsupported,
  /// Service ran out of the resources needed to complete the operation.
  OutOfResources,
  /// Operation invoked on an Entity that is not yet enabled.
  NotEnabled,
  /// Application attempted to modify an immutable QosPolicy.
  ImmutablePolicy, // can we check this statically?
  /// Application specified a set of policies that are not consistent with each other.
  InconsistentPolicy {
    reason: String,
  },
  /// A pre-condition for the operation was not met.
  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.
  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.
  LockPoisoned,

  /// Something that should not go wrong went wrong anyway.
  /// This is usually a bug in RustDDS
  Internal {
    reason: String,
  },

  Io {
    inner: std::io::Error,
  },
  Serialization {
    reason: String,
  },
  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) => {{
    error!($err_msg);
    Error::precondition_not_met($err_msg)
  }};
}

#[doc(hidden)]
#[macro_export]
macro_rules! log_and_err_internal {
  ($($arg:tt)*) => (
      { 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 From<std::io::Error> for Error {
  fn from(e: std::io::Error) -> Error {
    Error::Io { inner: e }
  }
}

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),
    }
  }
}