abi_stable/std_types/std_io.rs
1//! Ffi-safe equivalents of `std::io::{ErrorKind, Error, SeekFrom}`.
2#![allow(clippy::missing_const_for_fn)]
3
4use std::{
5 error::Error as ErrorTrait,
6 fmt::{self, Debug, Display},
7 io::{Error as ioError, ErrorKind, SeekFrom},
8};
9
10#[allow(unused_imports)]
11use core_extensions::SelfOps;
12
13use crate::{
14 std_types::{RBoxError, RNone, ROption, RSome},
15 traits::{IntoReprC, IntoReprRust},
16};
17
18///////////////////////////////////////////////////////////////////////////
19
20/// Ffi safe equivalent to `std::io::ErrorKind`.
21///
22/// Using a struct with associated constants is the
23/// ffi-safe way of doing `#[non_exhaustive]` field-less enums.
24#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
25#[repr(C)]
26#[derive(StableAbi)]
27pub struct RIoErrorKind {
28 value: u8,
29}
30
31macro_rules! impl_error_kind {
32 (
33 $(
34 $variant: ident, discriminant = $value: expr , message = $as_str_msg: expr ;
35 )*
36 ) => (
37 /// Every (visible) variant of RIoErrorKind, equivalent to that of `std::io::ErrorKind`.
38 #[allow(non_upper_case_globals)]
39 impl RIoErrorKind {
40 $(
41 ///
42 pub const $variant: Self = RIoErrorKind { value: $value };
43 )*
44 ///
45 pub const Other: Self = RIoErrorKind { value: 0 };
46 }
47
48 impl Debug for RIoErrorKind {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 let s = match *self {
51 $(
52 RIoErrorKind::$variant=> stringify!($variant),
53 )*
54 _ => "Other",
55 };
56 Display::fmt(s, f)
57 }
58 }
59
60 impl_from_rust_repr! {
61 impl From<ErrorKind> for RIoErrorKind {
62 fn(this){
63 match this {
64 $(
65 ErrorKind::$variant=> RIoErrorKind::$variant,
66 )*
67 _ => RIoErrorKind::Other,
68 }
69 }
70 }
71 }
72
73 impl_into_rust_repr! {
74 impl Into<ErrorKind> for RIoErrorKind {
75 fn(this){
76 match this {
77 $(
78 RIoErrorKind::$variant=> ErrorKind::$variant,
79 )*
80 _ => ErrorKind::Other,
81 }
82 }
83 }
84 }
85
86 impl RIoErrorKind {
87 pub(crate) fn error_message(&self) -> &'static str {
88 match *self {
89 $(
90 RIoErrorKind::$variant => $as_str_msg,
91 )*
92 _=> "other os error",
93 }
94 }
95 }
96 )
97}
98
99impl_error_kind! {
100 NotFound, discriminant = 1 , message = "entity not found" ;
101 PermissionDenied, discriminant = 2 , message = "permission denied" ;
102 ConnectionRefused, discriminant = 3 , message = "connection refused" ;
103 ConnectionReset, discriminant = 4 , message = "connection reset" ;
104 ConnectionAborted, discriminant = 5 , message = "connection aborted" ;
105 NotConnected, discriminant = 6 , message = "not connected" ;
106 AddrInUse, discriminant = 7 , message = "address in use" ;
107 AddrNotAvailable, discriminant = 8 , message = "address not available" ;
108 BrokenPipe, discriminant = 9 , message = "broken pipe" ;
109 AlreadyExists, discriminant = 10 , message = "entity already exists" ;
110 WouldBlock, discriminant = 11 , message = "operation would block" ;
111 InvalidInput, discriminant = 12 , message = "invalid input parameter" ;
112 InvalidData, discriminant = 13 , message = "invalid data" ;
113 TimedOut, discriminant = 14 , message = "timed out" ;
114 WriteZero, discriminant = 15 , message = "write zero" ;
115 Interrupted, discriminant = 16 , message = "operation interrupted" ;
116 UnexpectedEof, discriminant = 17 , message = "unexpected end of file" ;
117}
118
119///////////////////////////////////////////////////////////////////////////
120
121/// Ffi-safe equivalent of [`std::io::SeekFrom`].
122///
123/// [`std::io::SeekFrom`]: https://doc.rust-lang.org/std/io/enum.SeekFrom.html
124#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
125#[repr(u8)]
126#[derive(StableAbi)]
127pub enum RSeekFrom {
128 ///
129 Start(u64),
130 ///
131 End(i64),
132 ///
133 Current(i64),
134}
135
136impl_from_rust_repr! {
137 impl From<SeekFrom> for RSeekFrom {
138 fn(this){
139 match this {
140 SeekFrom::Start(x) => RSeekFrom::Start(x),
141 SeekFrom::End(x) => RSeekFrom::End(x),
142 SeekFrom::Current(x) => RSeekFrom::Current(x),
143 }
144 }
145 }
146}
147
148impl_into_rust_repr! {
149 impl Into<SeekFrom> for RSeekFrom {
150 fn(this){
151 match this {
152 RSeekFrom::Start(x) => SeekFrom::Start(x),
153 RSeekFrom::End(x) => SeekFrom::End(x),
154 RSeekFrom::Current(x) => SeekFrom::Current(x),
155 }
156 }
157 }
158}
159
160///////////////////////////////////////////////////////////////////////////
161
162/// Ffi safe equivalent to `std::io::Error`.
163///
164/// # Example
165///
166/// Defining an extern function to write a slice into a writer twice.
167///
168/// ```
169/// use abi_stable::{
170/// erased_types::interfaces::IoWriteInterface,
171/// rtry, sabi_extern_fn,
172/// std_types::{RIoError, ROk, RResult},
173/// traits::IntoReprC,
174/// DynTrait, RMut,
175/// };
176///
177/// use std::io::Write;
178///
179/// #[sabi_extern_fn]
180/// pub fn write_slice_twice(
181/// mut write: DynTrait<RMut<'_, ()>, IoWriteInterface>,
182/// slice: &[u8],
183/// ) -> RResult<(), RIoError> {
184/// rtry!(write.write_all(slice).into_c());
185/// rtry!(write.write_all(slice).into_c());
186/// ROk(())
187/// }
188///
189/// ```
190///
191#[repr(C)]
192#[derive(StableAbi)]
193pub struct RIoError {
194 kind: RIoErrorKind,
195 error: ROption<RBoxError>,
196}
197
198impl_from_rust_repr! {
199 impl From<ioError> for RIoError {
200 fn(this){
201 RIoError{
202 kind: this.kind().into(),
203 error: this.into_inner().map(RBoxError::from_box).into_c()
204 }
205 }
206 }
207}
208
209impl_into_rust_repr! {
210 impl Into<ioError> for RIoError {
211 fn(this){
212 let kind = this.kind().into_::<ErrorKind>();
213 match this.into_inner() {
214 Some(e) => ioError::new(kind, RBoxError::into_box(e)),
215 None => ioError::from(kind),
216 }
217 }
218 }
219}
220
221impl From<RIoErrorKind> for RIoError {
222 fn from(kind: RIoErrorKind) -> Self {
223 Self { kind, error: RNone }
224 }
225}
226
227impl From<ErrorKind> for RIoError {
228 fn from(kind: ErrorKind) -> Self {
229 Self {
230 kind: kind.into(),
231 error: RNone,
232 }
233 }
234}
235
236impl RIoError {
237 /// Constructs an `RIoError` from an error and a `std::io::ErrorKind`.
238 ///
239 /// # Example
240 ///
241 /// ```
242 /// use abi_stable::std_types::RIoError;
243 /// use std::io::ErrorKind;
244 ///
245 /// let err = RIoError::new(ErrorKind::Other, "".parse::<u64>().unwrap_err());
246 /// ```
247 pub fn new<E>(kind: ErrorKind, error: E) -> Self
248 where
249 E: ErrorTrait + Send + Sync + 'static,
250 {
251 RIoError {
252 kind: kind.into_c(),
253 error: RSome(RBoxError::new(error)),
254 }
255 }
256
257 /// Constructs an `RIoError` from a type convertible into a
258 /// `Box<dyn std::error::Error + Send + Sync + 'static>`, and a `std::io::ErrorKind`.
259 ///
260 /// # Example
261 ///
262 /// ```
263 /// use abi_stable::std_types::RIoError;
264 /// use std::io::ErrorKind;
265 ///
266 /// let str_err = "Timeout receiving the response from server.";
267 ///
268 /// let err = RIoError::new_(ErrorKind::TimedOut, str_err);
269 /// ```
270 #[inline]
271 pub fn new_<E>(kind: ErrorKind, error: E) -> Self
272 where
273 E: Into<Box<dyn ErrorTrait + Send + Sync + 'static>>,
274 {
275 Self::with_box(kind, error.into())
276 }
277
278 /// Constructs an `RIoError` from a `std::io::ErrorKind`.
279 ///
280 /// # Example
281 ///
282 /// ```
283 /// use abi_stable::std_types::RIoError;
284 /// use std::io::ErrorKind;
285 ///
286 /// let err = RIoError::from_kind(ErrorKind::AlreadyExists);
287 /// ```
288 pub fn from_kind(kind: ErrorKind) -> Self {
289 Self {
290 kind: kind.into_c(),
291 error: RNone,
292 }
293 }
294
295 /// Constructs an `RIoError` from a
296 /// `Box<dyn std::error::Error + Send + Sync + 'static>` and a `std::io::ErrorKind`.
297 ///
298 /// # Example
299 ///
300 /// ```
301 /// use abi_stable::std_types::RIoError;
302 /// use std::io::ErrorKind;
303 ///
304 /// let str_err = "Could not create file \"memes.txt\" because it already exists.";
305 ///
306 /// let err = RIoError::with_box(ErrorKind::AlreadyExists, str_err.into());
307 /// ```
308 pub fn with_box(kind: ErrorKind, error: Box<dyn ErrorTrait + Send + Sync + 'static>) -> Self {
309 RIoError {
310 kind: kind.into_c(),
311 error: RSome(RBoxError::from_box(error)),
312 }
313 }
314
315 /// Constructs an `RIoError` from an `RBoxError` and a `std::io::ErrorKind`.
316 ///
317 /// # Example
318 ///
319 /// ```
320 /// use abi_stable::std_types::RIoError;
321 /// use std::io::ErrorKind;
322 ///
323 /// type DynErr = Box<dyn std::error::Error + Send + Sync>;
324 ///
325 /// let str_err: DynErr = "IP address `256.256.256.256` is already in use.".into();
326 ///
327 /// let err = RIoError::with_rboxerror(ErrorKind::AddrInUse, str_err.into());
328 /// ```
329 pub fn with_rboxerror(kind: ErrorKind, error: RBoxError) -> Self {
330 RIoError {
331 kind: kind.into_c(),
332 error: RSome(error),
333 }
334 }
335
336 /// Retrieves the kind of io error.
337 ///
338 /// # Example
339 ///
340 /// ```
341 /// use abi_stable::std_types::{RIoError, RIoErrorKind};
342 /// use std::io::ErrorKind;
343 ///
344 /// let err = RIoError::from_kind(ErrorKind::AlreadyExists);
345 ///
346 /// assert_eq!(err.kind(), RIoErrorKind::AlreadyExists);
347 /// ```
348 pub fn kind(&self) -> RIoErrorKind {
349 self.kind
350 }
351
352 /// Gets the internal error,
353 /// returning `None` if this was constructed with `RIoError::from_kind`.
354 ///
355 /// # Example
356 ///
357 /// ```
358 /// use abi_stable::std_types::{RBoxError, RIoError, RIoErrorKind};
359 /// use std::io::ErrorKind;
360 ///
361 /// {
362 /// let err = RIoError::from_kind(ErrorKind::AlreadyExists);
363 /// assert_eq!(err.get_ref().map(|_| ()), None);
364 /// }
365 /// {
366 /// let msg = "Cannot access directory at \"/home/Steve/memes/\".";
367 /// let err = RIoError::new_(ErrorKind::PermissionDenied, msg);
368 ///
369 /// assert!(err.get_ref().is_some());
370 /// }
371 ///
372 /// ```
373 pub fn get_ref(&self) -> Option<&RBoxError> {
374 self.error.as_ref().into_rust()
375 }
376
377 /// Gets the internal error,
378 /// returning `None` if this was constructed with `RIoError::from_kind`.
379 ///
380 /// # Example
381 ///
382 /// ```
383 /// use abi_stable::std_types::{RBoxError, RIoError, RIoErrorKind};
384 /// use std::io::ErrorKind;
385 ///
386 /// {
387 /// let mut err = RIoError::from_kind(ErrorKind::AlreadyExists);
388 /// assert_eq!(err.get_mut().map(|_| ()), None);
389 /// }
390 /// {
391 /// let mut msg = "Cannot access directory at \"/home/Patrick/373.15K takes/\".";
392 /// let mut err = RIoError::new_(ErrorKind::PermissionDenied, msg);
393 /// assert!(err.get_mut().is_some());
394 /// }
395 ///
396 /// ```
397 pub fn get_mut(&mut self) -> Option<&mut RBoxError> {
398 self.error.as_mut().into_rust()
399 }
400
401 /// Converts this into the internal error,
402 /// returning `None` if this was constructed with `RIoError::from_kind`.
403 ///
404 /// # Example
405 ///
406 /// ```
407 /// use abi_stable::std_types::RIoError;
408 /// use std::io::ErrorKind;
409 ///
410 /// {
411 /// let err = RIoError::from_kind(ErrorKind::AlreadyExists);
412 /// assert_eq!(err.into_inner().map(|_| ()), None);
413 /// }
414 /// {
415 /// let mut msg = "Cannot access directory at \"/home/wo_boat/blog/\".";
416 /// let err = RIoError::new_(ErrorKind::PermissionDenied, msg);
417 /// assert!(err.into_inner().is_some());
418 /// }
419 ///
420 /// ```
421 pub fn into_inner(self) -> Option<RBoxError> {
422 self.error.into_rust()
423 }
424}
425
426impl Debug for RIoError {
427 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428 match self.error.as_ref() {
429 RSome(c) => Debug::fmt(&c, f),
430 RNone => f.debug_tuple("Kind").field(&self.kind).finish(),
431 }
432 }
433}
434
435impl Display for RIoError {
436 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437 match self.error.as_ref() {
438 RSome(c) => Display::fmt(&c, f),
439 RNone => Display::fmt(self.kind.error_message(), f),
440 }
441 }
442}
443
444impl ErrorTrait for RIoError {}
445
446///////////////////////////////////////////////////////////////////////////////////
447
448#[cfg(all(test, not(feature = "only_new_tests")))]
449mod error_kind_tests {
450 use super::*;
451
452 #[test]
453 fn conversions() {
454 for (from, to) in [
455 (ErrorKind::NotConnected, RIoErrorKind::NotConnected),
456 (ErrorKind::AddrInUse, RIoErrorKind::AddrInUse),
457 (ErrorKind::Other, RIoErrorKind::Other),
458 ] {
459 assert_eq!(RIoErrorKind::from(from), to);
460 assert_eq!(to.into_::<ErrorKind>(), from);
461 }
462 }
463}
464
465#[cfg(all(test, not(feature = "only_new_tests")))]
466mod io_error_tests {
467 use super::*;
468
469 use crate::test_utils::{check_formatting_equivalence, deref_address, Stringy};
470
471 #[test]
472 fn from_error_kind() {
473 for kind in [
474 ErrorKind::NotConnected,
475 ErrorKind::AddrInUse,
476 ErrorKind::Other,
477 ] {
478 let err = kind.piped(RIoError::from_kind);
479
480 assert_eq!(err.kind(), kind.into_c());
481 }
482 }
483
484 #[test]
485 fn from_value() {
486 let err = Stringy::new("What\nis\ra\tline");
487 let e0 = RIoError::new(ErrorKind::Other, err.clone());
488
489 check_formatting_equivalence(&err, &e0);
490 }
491
492 #[test]
493 fn from_boxerror() {
494 let err = Stringy::new("What\nis\ra\tline");
495 let box_ = err.clone().piped(Box::new);
496 let addr = deref_address(&box_);
497 let ioerr = RIoError::with_box(ErrorKind::Other, box_);
498
499 check_formatting_equivalence(&err, &ioerr);
500
501 assert_eq!(
502 addr,
503 ioerr
504 .into_inner()
505 .unwrap()
506 .into_box()
507 .piped_ref(deref_address)
508 );
509 }
510
511 #[test]
512 fn from_rboxerror() {
513 let err = Stringy::new("What\nis\ra\tline");
514 let rbox = err.clone().piped(RBoxError::new);
515 let addr = rbox.heap_address();
516 let mut ioerr = RIoError::with_rboxerror(ErrorKind::Other, rbox);
517
518 check_formatting_equivalence(&err, &ioerr);
519
520 assert_eq!(addr, ioerr.get_ref().unwrap().heap_address());
521
522 assert_eq!(addr, ioerr.get_mut().unwrap().heap_address());
523
524 assert_eq!(addr, ioerr.into_inner().unwrap().heap_address());
525 }
526}