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}