rustdds/dds/
result.rs

1//! This module corresponds to "Return codes" in DDS spec Section "2.2.1.1
2//!
3//! Format and Conventions", but the error codes are not the same.
4//! In particular, a uniform error type is not used for all DDS calls, because
5//! most calls can only return a subset of errors.
6//! Using specialized error types makes the description of possible failures
7//! more precise.
8
9use std::sync::PoisonError;
10
11use crate::{no_key::wrappers::NoKeyWrapper, serialization, TopicKind};
12#[cfg(feature = "security")]
13use crate::security::SecurityError;
14
15/// Error type for DDS "read" type operations.
16#[derive(Debug, thiserror::Error)]
17pub enum ReadError {
18  /// Data received over RTPS could not be decoded.
19  /// Reason field gives more details on what went wrong.
20  #[error("Deserialization error: {reason}")]
21  Deserialization { reason: String },
22
23  /// DDS received a data instance dispose message via RTPS, but was asked to
24  /// dispose an instance, which we do not have.
25  ///
26  /// If that data instance was published and last available for reading before
27  /// this DataReader joined the domain (i.e. was started), then it is normal
28  /// that we do not know it. Otherwise, it could be a symptom of communication
29  /// or serialization error.
30  #[error("Received dispose message with unknown key: {details}")]
31  UnknownKey { details: String },
32
33  /// Communication or synchronization with RTPS processing thread or Discovery
34  /// thread fails. This is most likely because either thread has panicked or
35  /// gotten stuck somewhere, neither of which is supposed to happen. This is
36  /// typically not recoverable, except by starting a new DomainParticipant.
37  #[error(
38    "Synchronization failed due poisoning. Another thread may have panicked. Details: {reason}"
39  )]
40  Poisoned { reason: String },
41
42  /// Something that should not go wrong went wrong anyway.
43  /// This is usually a bug in RustDDS
44  #[error("Internal error: {reason}")]
45  Internal { reason: String },
46}
47
48#[doc(hidden)]
49#[macro_export]
50macro_rules! read_error_deserialization {
51  ($($arg:tt)*) => (
52      { log::error!($($arg)*);
53        Err( ReadError::Deserialization{ reason: format!($($arg)*) } )
54      }
55    )
56}
57
58#[doc(hidden)]
59#[macro_export]
60macro_rules! read_error_unknown_key {
61  ($($arg:tt)*) => (
62      { log::error!($($arg)*);
63        Err( ReadError::UnknownKey{ reason: format!($($arg)*) } )
64      }
65    )
66}
67
68#[doc(hidden)]
69#[macro_export]
70macro_rules! read_error_poisoned {
71  ($($arg:tt)*) => (
72      { log::error!($($arg)*);
73        Err( ReadError::Poisoned{ reason: format!($($arg)*) } )
74      }
75    )
76}
77
78#[doc(hidden)]
79#[macro_export]
80macro_rules! read_error_internal {
81  ($($arg:tt)*) => (
82      { log::error!($($arg)*);
83        Err( ReadError::Internal{ reason: format!($($arg)*) } )
84      }
85    )
86}
87
88impl From<serialization::Error> for ReadError {
89  fn from(e: serialization::Error) -> Self {
90    ReadError::Deserialization {
91      reason: e.to_string(),
92    }
93  }
94}
95
96/// This is a specialized Result, similar to [`std::io::Result`].
97pub type ReadResult<T> = std::result::Result<T, ReadError>;
98
99/// Error type for DDS "Write" operations.
100///
101/// Note: This type contains payload data type `D`. This means that `WriteError`
102/// implements `Debug` only if `D` does.
103#[derive(Debug, thiserror::Error)]
104pub enum WriteError<D> {
105  /// Data serializer (`SerializerAdapter`) reported an error when called.
106  /// Reason field gives more details on what went wrong.
107  #[error("Serialization error: {reason}")]
108  Serialization { reason: String, data: D },
109
110  /// Communication or synchronization with RTPS processing thread or Discovery
111  /// thread fails. This is most likely because either thread has panicked or
112  /// gotten stuck somewhere, neither of which is supposed to happen. This is
113  /// typically not recoverable, except by starting a new DomainParticipant.
114  #[error("Cannot communicate. Background thread may have panicked: {reason}")]
115  Poisoned { reason: String, data: D },
116
117  /// a [`std::io::Error`] occurred within RustDDS.
118  #[error("std:io:Error {0}")]
119  Io(#[from] std::io::Error),
120
121  /// The operation would block, or blocked until specified timeout expired.
122  #[error("Write operation timed out while blocking")]
123  WouldBlock { data: D },
124
125  /// Something that should not go wrong went wrong anyway.
126  /// This is usually a bug in RustDDS
127  #[error("Internal error: {reason}")]
128  Internal { reason: String },
129}
130
131impl<T> From<PoisonError<T>> for WriteError<()> {
132  fn from(poison_error: PoisonError<T>) -> Self {
133    Self::Poisoned {
134      reason: poison_error.to_string(),
135      data: (),
136    }
137  }
138}
139
140impl From<serialization::Error> for WriteError<()> {
141  fn from(e: serialization::Error) -> Self {
142    WriteError::Serialization {
143      reason: e.to_string(),
144      data: (),
145    }
146  }
147}
148
149impl<D> WriteError<D> {
150  /// Forgets the data of WriteError, which can be useful in cases where it is
151  /// not needed.
152  pub fn forget_data(self) -> WriteError<()> {
153    match self {
154      WriteError::Serialization { reason, data: _ } => {
155        WriteError::Serialization { reason, data: () }
156      }
157      WriteError::Poisoned { reason, data: _ } => WriteError::Poisoned { reason, data: () },
158      WriteError::Io(e) => WriteError::Io(e),
159      WriteError::WouldBlock { data: _ } => WriteError::WouldBlock { data: () },
160      WriteError::Internal { reason } => WriteError::Internal { reason },
161    }
162  }
163}
164
165/// This is a specialized Result, similar to [`std::io::Result`].
166pub type WriteResult<T, D> = std::result::Result<T, WriteError<D>>;
167
168pub(crate) fn unwrap_no_key_write_error<D>(
169  no_key_write_error: WriteError<NoKeyWrapper<D>>,
170) -> WriteError<D> {
171  match no_key_write_error {
172    WriteError::Serialization { reason, data } => WriteError::Serialization {
173      reason,
174      data: data.d,
175    },
176    WriteError::Poisoned { reason, data } => WriteError::Poisoned {
177      reason,
178      data: data.d,
179    },
180    WriteError::WouldBlock { data } => WriteError::WouldBlock { data: data.d },
181    WriteError::Internal { reason } => WriteError::Internal { reason },
182    WriteError::Io(io) => WriteError::Io(io),
183  }
184}
185
186/// Error type for object creation operations.
187#[derive(Debug, thiserror::Error)]
188pub enum CreateError {
189  #[error("Object creation failed, because necessary resource has been dropped: {reason}")]
190  ResourceDropped { reason: String },
191
192  #[error("Cannot communicate. Background thread may have panicked: {reason}")]
193  Poisoned { reason: String },
194
195  #[error("std:io:Error {0}")]
196  Io(#[from] std::io::Error),
197
198  #[error("Wrong Topic kind. Expected {0}")]
199  TopicKind(TopicKind),
200
201  /// Something that should not go wrong went wrong anyway.
202  /// This is usually a bug in RustDDS
203  #[error("Internal error: {reason}")]
204  Internal { reason: String },
205
206  #[error("Invalid call parameter: {reason}")]
207  BadParameter { reason: String },
208
209  #[error("Resource allocation failed: {reason}")]
210  OutOfResources { reason: String },
211
212  #[cfg(feature = "security")]
213  #[error("Not allowed by security: {reason}")]
214  NotAllowedBySecurity { reason: String },
215}
216
217#[doc(hidden)]
218#[macro_export]
219macro_rules! create_error_dropped {
220  ($($arg:tt)*) => (
221      { log::error!($($arg)*);
222        Err( CreateError::ResourceDropped{ reason: format!($($arg)*) } )
223      }
224    )
225}
226
227#[doc(hidden)]
228#[macro_export]
229macro_rules! create_error_poisoned {
230  ($($arg:tt)*) => (
231      { log::error!($($arg)*);
232        Err( CreateError::Poisoned{ reason: format!($($arg)*) } )
233      }
234    )
235}
236
237#[doc(hidden)]
238#[macro_export]
239macro_rules! create_error_internal {
240  ($($arg:tt)*) => (
241      { log::error!($($arg)*);
242        Err( CreateError::Internal{ reason: format!($($arg)*) } )
243      }
244    )
245}
246
247#[doc(hidden)]
248#[macro_export]
249macro_rules! create_error_bad_parameter {
250  ($($arg:tt)*) => (
251      { log::error!($($arg)*);
252        Err( CreateError::BadParameter{ reason: format!($($arg)*) } )
253      }
254    )
255}
256
257#[doc(hidden)]
258#[macro_export]
259macro_rules! create_error_out_of_resources {
260  ($($arg:tt)*) => (
261      { log::error!($($arg)*);
262        Err( CreateError::OutOfResources{ reason: format!($($arg)*) } )
263      }
264    )
265}
266
267#[doc(hidden)]
268#[cfg(feature = "security")]
269#[macro_export]
270macro_rules! create_error_not_allowed_by_security {
271  ($($arg:tt)*) => (
272      { log::error!($($arg)*);
273        Err( CreateError::NotAllowedBySecurity{ reason: format!($($arg)*) } )
274      }
275    )
276}
277
278impl<T> From<PoisonError<T>> for CreateError {
279  fn from(poison_error: PoisonError<T>) -> Self {
280    Self::Poisoned {
281      reason: poison_error.to_string(),
282    }
283  }
284}
285
286#[cfg(feature = "security")]
287impl From<SecurityError> for CreateError {
288  fn from(security_error: SecurityError) -> Self {
289    CreateError::NotAllowedBySecurity {
290      reason: security_error.to_string(),
291    }
292  }
293}
294
295/// This is a specialized Result, similar to [`std::io::Result`].
296pub type CreateResult<T> = std::result::Result<T, CreateError>;
297
298#[derive(Debug, thiserror::Error)]
299pub enum WaitError {
300  #[error("Waiting timed out")]
301  Timeout,
302}
303
304pub type WaitResult<T> = std::result::Result<T, WaitError>;
305
306#[derive(Debug, thiserror::Error)]
307pub enum QosError {
308  #[error("Parameter value or combination of values was bad. Details: {details}")]
309  BadParameter { details: String },
310}