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}