scratchpad/
error.rs

1// Copyright 2018-2021 Theodore Cipicchio
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Error support.
10
11use core::fmt;
12use core::ptr;
13
14use core::mem::forget;
15
16/// Categories of errors during scratchpad operations.
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub enum ErrorKind {
19    /// Maximum number of scratchpad markers are currently set.
20    MarkerLimit,
21    /// Allocation cannot be made because the marker is not the most-recently
22    /// created active marker.
23    MarkerLocked,
24    /// Insufficient space in the scratchpad buffer for the allocation.
25    InsufficientMemory,
26    /// Allocation cannot be extended since it is not the most recent
27    /// allocation in its marker.
28    NotAtEnd,
29    /// Allocations being merged are not in order.
30    OutOfOrder,
31    /// Allocations being merged are not adjacent in memory.
32    NotAdjacent,
33    /// Integer overflow detected (typically due to a very large size or
34    /// alignment).
35    Overflow,
36}
37
38/// The error type for scratchpad operations.
39///
40/// Various scratchpad operations require ownership of some or all of their
41/// parameters to be transferred to the callee, such as for storage in a new
42/// allocation. If such operations fail, the caller may still want to do
43/// something with the data originally provided.
44///
45/// This type encapsulates both the kind of error that occurred and any
46/// recoverable "owned" parameters that were passed so that the caller can
47/// have them back.
48#[derive(Debug)]
49pub struct Error<T> {
50    /// "Owned" arguments passed to the function.
51    args: T,
52    /// Type of error that occurred.
53    kind: ErrorKind,
54}
55
56impl<T> Error<T> {
57    /// Creates a new error with the specified category and argument values.
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// use scratchpad::{Error, ErrorKind};
63    ///
64    /// // Error containing multiple recycled arguments.
65    /// let multi_error = Error::new(
66    ///     ErrorKind::MarkerLocked,
67    ///     (3.14159f32, 2.71828f32),
68    /// );
69    ///
70    /// // Error containing a single recycled argument. A tuple is still used
71    /// // to remain consistent with errors that recycle multiple argument
72    /// // values.
73    /// let single_error = Error::new(ErrorKind::MarkerLocked, (3.14159f32,));
74    ///
75    /// // Error containing no recycled argument values. A unit is used by the
76    /// // crate to signify an empty set of arguments.
77    /// let simple_error = Error::new(ErrorKind::MarkerLocked, ());
78    /// ```
79    #[inline]
80    pub fn new(kind: ErrorKind, args: T) -> Self {
81        Error { args, kind }
82    }
83
84    /// Returns the category of error that occurred.
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// use scratchpad::{Error, ErrorKind};
90    ///
91    /// let error = Error::new(
92    ///     ErrorKind::MarkerLocked,
93    ///     (3.14159f32, 2.71828f32),
94    /// );
95    ///
96    /// let kind = error.kind();
97    /// assert_eq!(kind, ErrorKind::MarkerLocked);
98    /// ```
99    #[inline]
100    pub fn kind(&self) -> ErrorKind {
101        self.kind
102    }
103
104    /// Returns a tuple containing values that would have passed ownership to
105    /// the callee if the operation was successful.
106    ///
107    /// While there's no constraint on the arguments type, this crate will
108    /// only ever produce errors that have either a [unit] or [tuple] for its
109    /// arguments, even if only a single value is returned to the caller.
110    ///
111    /// # Examples
112    ///
113    /// ```
114    /// use scratchpad::{Error, ErrorKind};
115    ///
116    /// let error = Error::new(
117    ///     ErrorKind::MarkerLocked,
118    ///     (3.14159f32, 2.71828f32),
119    /// );
120    ///
121    /// let args = error.args();
122    /// assert_eq!(args, &(3.14159f32, 2.71828f32));
123    /// ```
124    ///
125    /// [unit]: https://doc.rust-lang.org/std/primitive.unit.html
126    /// [tuple]: https://doc.rust-lang.org/std/primitive.tuple.html
127    #[inline]
128    pub fn args(&self) -> &T {
129        &self.args
130    }
131
132    /// Unwraps the error, yielding a tuple containing values that would have
133    /// passed ownership to the callee if the operation was successful.
134    ///
135    /// # Examples
136    ///
137    /// ```
138    /// use scratchpad::{Error, ErrorKind};
139    ///
140    /// let error = Error::new(
141    ///     ErrorKind::MarkerLocked,
142    ///     (3.14159f32, 2.71828f32),
143    /// );
144    ///
145    /// let (x, y) = error.unwrap_args();
146    /// assert_eq!(x, 3.14159f32);
147    /// assert_eq!(y, 2.71828f32);
148    /// ```
149    #[inline]
150    pub fn unwrap_args(self) -> T {
151        unsafe {
152            let args = ptr::read(&self.args);
153            forget(self);
154            args
155        }
156    }
157
158    /// Maps an `Error<T>` to an `Error<U>` by applying a function to the
159    /// contained arguments value, leaving the `ErrorKind` value untouched.
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// use scratchpad::{Error, ErrorKind};
165    ///
166    /// let error = Error::new(ErrorKind::MarkerLocked, (-12, 23));
167    ///
168    /// let new_error = error.map(|args| (args.1 as i64 * -2,));
169    /// assert_eq!(new_error.args().0, -46i64);
170    /// ```
171    #[inline]
172    pub fn map<U, F>(self, op: F) -> Error<U>
173    where
174        F: FnOnce(T) -> U,
175    {
176        let kind = self.kind;
177        let args = op(self.unwrap_args());
178        Error { args, kind }
179    }
180}
181
182impl<T> fmt::Display for Error<T> {
183    #[inline]
184    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185        match self.kind {
186            ErrorKind::MarkerLimit => {
187                write!(f, "scratchpad marker limit reached")
188            }
189            ErrorKind::MarkerLocked => {
190                write!(f, "marker is not the most recent active marker")
191            }
192            ErrorKind::InsufficientMemory => {
193                write!(f, "insufficient allocation buffer space")
194            }
195            ErrorKind::NotAtEnd => {
196                write!(f, "allocation not most recent from its marker")
197            }
198            ErrorKind::OutOfOrder => {
199                write!(f, "allocations specified out-of-order")
200            }
201            ErrorKind::NotAdjacent => {
202                write!(f, "allocations are not adjacent in memory")
203            }
204            ErrorKind::Overflow => write!(f, "integer overflow"),
205        }
206    }
207}
208
209#[cfg(feature = "std")]
210impl<T> ::std::error::Error for Error<T>
211where
212    T: fmt::Debug,
213{
214    #[inline]
215    fn description(&self) -> &str {
216        match self.kind {
217            ErrorKind::MarkerLimit => "scratchpad marker limit reached",
218            ErrorKind::MarkerLocked => {
219                "marker is not the most recent active marker"
220            }
221            ErrorKind::InsufficientMemory => {
222                "insufficient allocation buffer space"
223            }
224            ErrorKind::NotAtEnd => {
225                "allocation not most recent from its marker"
226            }
227            ErrorKind::OutOfOrder => "allocations specified out-of-order",
228            ErrorKind::NotAdjacent => {
229                "allocations are not adjacent in memory"
230            }
231            ErrorKind::Overflow => "integer overflow",
232        }
233    }
234}