vmap/
error.rs

1//! Types for working with various map operation errors.
2
3use std::{fmt, io};
4
5/// A specialized `Result` type for map operations.
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// A specialiazed `Result` type for conversion operations.
9///
10/// The origin `self` type is consumed When converting between [`Map`]
11/// and [`MapMut`] types. The `ConvertResult` returns the original input
12/// value on failure so that it isn't necessarily dropped. This allows
13/// a failure to be handled while stil maintaining the existing mapping.
14///
15/// [`Map`]: struct.Map.html
16/// [`MapMut`]: struct.MapMut.html
17pub type ConvertResult<T, F> = std::result::Result<T, (Error, F)>;
18
19impl<F> From<(Error, F)> for Error {
20    /// Converts the `(Error, F)` tuple from a [`ConvertResult`] result into
21    /// an [`Error`], dropping the failed map in the process.
22    ///
23    /// [`ConvertResult`]: type.ConvertResult.html
24    /// [`Error`]: type.Error.html
25    fn from(value: (Error, F)) -> Error {
26        value.0
27    }
28}
29
30/// A list specifying general categories of map errors.
31///
32/// These errors can be converted into `std::io::Error` values.
33///
34/// # Examples
35///
36/// ```should_panic
37/// fn create_map() -> vmap::Result<vmap::Map> {
38///     /// ...
39/// # use vmap::{Error, Operation, Input};
40/// # Err(Error::input(Operation::MapFile, Input::InvalidRange))
41/// }
42///
43/// fn main() -> std::io::Result<()> {
44///     let map = create_map()?;
45///     println!("len = {}\n", map.len());
46///     Ok(())
47/// }
48/// ```
49pub struct Error {
50    repr: Repr,
51    op: Operation,
52}
53
54enum Repr {
55    Io(io::Error),
56    Input(Input),
57    System(system_error::Error),
58}
59
60impl Error {
61    /// Returns an error that wraps a `std::io::Error` along with an [`Operation`].
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use std::io::ErrorKind;
67    /// use vmap::{Error, Operation};
68    ///
69    /// println!("I/O error: {:?}", Error::io(
70    ///     Operation::MapFile,
71    ///     ErrorKind::NotFound.into(),
72    /// ));
73    /// ```
74    ///
75    /// [`Operation`]: enum.Operation.html
76    pub fn io(op: Operation, err: io::Error) -> Self {
77        Self {
78            repr: Repr::Io(err),
79            op,
80        }
81    }
82
83    /// Returns an error that wraps an [`Input`] type along with an [`Operation`].
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// use vmap::{Error, Operation, Input};
89    ///
90    /// println!("Input error: {:?}", Error::input(
91    ///     Operation::MapFile,
92    ///     Input::InvalidRange,
93    /// ));
94    /// ```
95    ///
96    /// [`Input`]: enum.Input.html
97    /// [`Operation`]: enum.Operation.html
98    pub fn input(op: Operation, input: Input) -> Self {
99        Self {
100            repr: Repr::Input(input),
101            op,
102        }
103    }
104
105    /// Returns an error that wraps a `system_error::Error` along with an [`Operation`].
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use std::io::ErrorKind;
111    /// use vmap::{Error, Operation};
112    ///
113    /// println!("System error: {:?}", Error::system(
114    ///     Operation::MapFile,
115    ///     system_error::Error::last_os_error()
116    /// ));
117    /// ```
118    ///
119    /// [`system_error::Error`]: https://docs.rs/system_error/0.1.1/system_error/struct.Error.html
120    /// [`Operation`]: enum.Operation.html
121    pub fn system(op: Operation, err: system_error::Error) -> Self {
122        Self {
123            repr: Repr::System(err),
124            op,
125        }
126    }
127
128    /// Returns an error that wraps a [`system_error::KernelCode`] along with an [`Operation`].
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// use vmap::{Error, Operation};
134    ///
135    /// println!("Kernel error: {:?}", Error::kernel(
136    ///     Operation::RingAllocate,
137    ///     1,
138    /// ));
139    /// ```
140    ///
141    /// [`system_error::KernelCode`]: https://docs.rs/system_error/0.1.1/system_error/type.KernelCode.html
142    /// [`Operation`]: enum.Operation.html
143    pub fn kernel(op: Operation, code: system_error::KernelCode) -> Self {
144        Self::system(op, system_error::Error::from_raw_kernel_error(code))
145    }
146
147    /// Returns an error representing the last OS error which occurred.
148    ///
149    /// This function reads the value of `errno` for the target platform (e.g.
150    /// `GetLastError` on Windows) and will return a corresponding instance of
151    /// `Error` for the error code.
152    ///
153    /// # Examples
154    ///
155    /// ```
156    /// use vmap::{Error, Operation};
157    ///
158    /// println!("last OS error: {:?}", Error::last_os_error(Operation::MapFile));
159    /// ```
160    pub fn last_os_error(op: Operation) -> Self {
161        Self::system(op, system_error::Error::last_os_error())
162    }
163
164    /// Returns the OS error that this error represents (if any).
165    ///
166    /// If this `Error` was constructed via `last_os_error`, then this function
167    /// will return `Some`, otherwise it will return `None`.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use vmap::{Error, Input, Operation};
173    ///
174    /// fn print_os_error(err: &Error) {
175    ///     if let Some(raw_os_err) = err.raw_os_error() {
176    ///         println!("raw OS error: {:?}", raw_os_err);
177    ///     } else {
178    ///         println!("Not an OS error");
179    ///     }
180    /// }
181    ///
182    /// // Will print "raw OS error: ...".
183    /// print_os_error(&Error::last_os_error(Operation::MapFile));
184    /// // Will print "Not an OS error".
185    /// print_os_error(&Error::input(Operation::MapFile, Input::InvalidRange));
186    /// ```
187    pub fn raw_os_error(&self) -> Option<i32> {
188        match &self.repr {
189            Repr::Io(err) => err.raw_os_error(),
190            Repr::Input(_) => None,
191            Repr::System(err) => err.raw_os_error(),
192        }
193    }
194
195    /// Returns the corresponding `std::io::ErrorKind` for this error.
196    ///
197    /// # Examples
198    ///
199    /// ```
200    /// use std::io::ErrorKind;
201    /// use vmap::{Error, Operation};
202    ///
203    /// fn print_error(err: Error) {
204    ///     println!("{:?}", err.kind());
205    /// }
206    ///
207    /// // Will print "Other".
208    /// print_error(Error::last_os_error(Operation::MapFile));
209    /// // Will print "NotFound".
210    /// print_error(Error::io(Operation::MapFile, ErrorKind::NotFound.into()));
211    /// ```
212    pub fn kind(&self) -> io::ErrorKind {
213        match self.repr {
214            Repr::Io(ref err) => err.kind(),
215            Repr::Input(_) => io::ErrorKind::InvalidInput,
216            Repr::System(ref err) => err.kind(),
217        }
218    }
219
220    /// Returns the corresponding [`Operation`] that cuased the error.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// use vmap::{Error, Operation};
226    ///
227    /// fn print_operation(err: Error) {
228    ///     println!("{:?}", err.operation());
229    /// }
230    ///
231    /// // Will print "MapFile".
232    /// print_operation(Error::last_os_error(Operation::MapFile));
233    /// ```
234    ///
235    /// [`Operation`]: enum.Operation.html
236    pub fn operation(&self) -> Operation {
237        self.op
238    }
239}
240
241impl std::error::Error for Error {
242    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
243        match self.repr {
244            Repr::Io(ref err) => Some(err),
245            Repr::Input(_) => None,
246            Repr::System(_) => None,
247        }
248    }
249}
250
251impl From<std::io::Error> for Error {
252    fn from(err: std::io::Error) -> Self {
253        Self {
254            repr: Repr::Io(err),
255            op: Operation::None,
256        }
257    }
258}
259
260impl From<Error> for std::io::Error {
261    fn from(err: Error) -> Self {
262        match err.repr {
263            Repr::Io(io) => io,
264            Repr::Input(v) => Self::new(io::ErrorKind::InvalidInput, v.as_str()),
265            Repr::System(sys) => sys.into(),
266        }
267    }
268}
269
270impl fmt::Debug for Error {
271    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
272        let (field, value) = match self.repr {
273            Repr::Io(ref err) => ("io", err as &dyn fmt::Debug),
274            Repr::Input(ref input) => ("input", input as &dyn fmt::Debug),
275            Repr::System(ref err) => ("system", err as &dyn fmt::Debug),
276        };
277        fmt.debug_struct("Error")
278            .field("op", &self.op)
279            .field("kind", &self.kind())
280            .field(field, value)
281            .finish()
282    }
283}
284
285impl fmt::Display for Error {
286    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
287        let value = match self.repr {
288            Repr::Io(ref err) => err as &dyn fmt::Display,
289            Repr::Input(ref input) => input as &dyn fmt::Display,
290            Repr::System(ref err) => err as &dyn fmt::Display,
291        };
292        if let Some(op) = self.op.as_str() {
293            write!(fmt, "failed to {}, {}", op, value)
294        } else {
295            value.fmt(fmt)
296        }
297    }
298}
299
300/// A list specifying general categories of erroneous operations.
301///
302/// This list is intended to grow over time and it is not recommended to
303/// exhaustively match against it.
304///
305/// It is used with the [`Error`] type.
306///
307/// [`Error`]: struct.Error.html
308#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
309#[non_exhaustive]
310pub enum Operation {
311    /// The operation failed while attempting to map a file.
312    MapFile,
313    /// A map file handle failed to open.
314    MapFileHandle,
315    /// The view for a map file handle could not be created.
316    MapFileView,
317    /// The operation failed while attempting to allocate an anonymous mapping.
318    MapAnonymous,
319    /// An anonymous mapping handle failed to open.
320    MapAnonymousHandle,
321    /// The view for an anonymouse mapping handle could not be created.
322    MapAnonymousView,
323    /// A pointer could not be unmapped.
324    Unmap,
325    /// The [`Protect`] could not be applied to the provided memory region.
326    ///
327    /// [`Protect`]: ../enum.Protect.html
328    Protect,
329    /// The [`Advise`] could not be applied to the provided memory region.
330    ///
331    /// [`Advise`]: ../enum.Advise.html
332    Advise,
333    /// The physical page could not be locked into memory.
334    Lock,
335    /// The physical page could not be unlocked from memory.
336    Unlock,
337    /// A flush cannot be perfomed for the provided input.
338    Flush,
339    /// The full address space for a ring could not be allocated.
340    RingAllocate,
341    /// The full address space for a ring could not be deallocated.
342    RingDeallocate,
343    /// A virtual mapping entry could not be created.
344    RingEntry,
345    /// The mapping for the first half of the ring failed to allocate.
346    RingPrimary,
347    /// The mapping for the second half of the ring failed to allocate.
348    RingSecondary,
349    /// A temporary memory file descriptor failed to open.
350    MemoryFd,
351    /// Used for pure I/O errors to simplify wrapping a `std::io::Error` into an
352    ///
353    /// [`Error`]: struct.Error.html
354    None,
355}
356
357impl Operation {
358    /// Returns a display message fragment describing the `Operation` type.
359    ///
360    /// The result of `as_str` is used to `Display` the `Operation`.
361    ///
362    /// # Examples
363    ///
364    /// ```
365    /// use vmap::Operation;
366    ///
367    /// fn print_operation(op: Operation) {
368    ///     println!("failed to {}", op.as_str().unwrap());
369    /// }
370    ///
371    /// // Will print "failed to map file".
372    /// print_operation(Operation::MapFile);
373    /// ```
374    pub fn as_str(&self) -> Option<&'static str> {
375        match *self {
376            Operation::MapFile => Some("map file"),
377            Operation::MapFileHandle => Some("map file handle"),
378            Operation::MapFileView => Some("map file view"),
379            Operation::MapAnonymous => Some("map anonymous"),
380            Operation::MapAnonymousHandle => Some("map anonymous handle"),
381            Operation::MapAnonymousView => Some("map anonymous view"),
382            Operation::Unmap => Some("unmap"),
383            Operation::Protect => Some("protect mapped memory"),
384            Operation::Advise => Some("advise mapped memory"),
385            Operation::Lock => Some("lock mapped memory"),
386            Operation::Unlock => Some("unlock mapped memory"),
387            Operation::Flush => Some("flush mapped memory"),
388            Operation::RingAllocate => Some("allocate full ring"),
389            Operation::RingDeallocate => Some("deallocate full ring"),
390            Operation::RingEntry => Some("make ring memory entry"),
391            Operation::RingPrimary => Some("map ring first half"),
392            Operation::RingSecondary => Some("map ring second half"),
393            Operation::MemoryFd => Some("open memory fd"),
394            Operation::None => None,
395        }
396    }
397}
398
399impl fmt::Display for Operation {
400    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
401        fmt.write_str(self.as_str().unwrap_or(""))
402    }
403}
404
405/// A list specifying general categories of input mapping errors.
406///
407/// This list is intended to grow over time and it is not recommended to
408/// exhaustively match against it.
409///
410/// It is used with the [`Error`] type.
411///
412/// [`Error`]: struct.Error.html
413#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
414#[non_exhaustive]
415pub enum Input {
416    /// The range of the requested file or bytes is invalid.
417    InvalidRange,
418}
419
420impl Input {
421    /// Returns a display message fragment describing the `Input` type.
422    ///
423    /// The result of `as_str` is used to `Display` the `Input`.
424    ///
425    /// # Examples
426    ///
427    /// ```
428    /// use vmap::{Input, Operation};
429    ///
430    /// fn print_input(op: Operation, input: Input) {
431    ///     println!("failed to {}, {}", op.as_str().unwrap(), input.as_str());
432    /// }
433    ///
434    /// // Will print "failed to map file, invalid range"
435    /// print_input(Operation::MapFile, Input::InvalidRange);
436    /// ```
437    pub fn as_str(&self) -> &'static str {
438        match *self {
439            Input::InvalidRange => "invalid range",
440        }
441    }
442}
443
444impl fmt::Display for Input {
445    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
446        fmt.write_str(self.as_str())
447    }
448}