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}