1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//
// Copyright 2019, Niels Sascha Reedijk <niels.reedijk@gmail.com>
// All rights reserved. Distributed under the terms of the MIT License.
//

use std::{error, fmt, result, str};
use std::ffi::CStr;

use haiku_sys::status_t;
use haiku_sys::errors::*;
use libc::{c_int, c_char, size_t};

/// This is a shortened version for a standard Rust result that returns a
/// Haiku error.
///
/// It is used throughout the API, except for the storage kit, which reuses
/// the `std::io::Result` type.
pub type Result<T> = result::Result<T, HaikuError>;

/// This struct represents an Error for using this API
///
/// The error is very much based on the standard library's `std::io::Error`,
/// and roughly has the same usage and functionality. 
pub struct HaikuError {
	repr: Repr,
}

impl fmt::Debug for HaikuError {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		fmt::Debug::fmt(&self.repr, f)
	}
}

enum Repr{
	Os(status_t),
	Simple(ErrorKind),
	Custom(Box<Custom>)
}

#[derive(Debug)]
struct Custom {
	kind: ErrorKind,
	error: Box<dyn error::Error+Send+Sync>,
}

#[derive(Clone, Copy, Debug)]
/// The kind of error that occured
///
/// Note that this list is not complete, there might be more error kinds added
/// in the future.
pub enum ErrorKind {
	/// An operation was (prematurely) interrupted by another system event.
	/// Usually, you can retry the operation in these instances.
	Interrupted,
	/// This error is returned if the function cannot return valid data, for
	/// example due to a system error.
	InvalidData,
	/// This error tells that the user is supplying parameters that are not
	/// valid.
	InvalidInput,
	/// This error is returned when one of the parameters of the function call
	/// refers to something that does not/no longer exists.
	NotFound,
	/// This error is returned when the operation is not allowed, because the
	/// user has insufficient privileges, or the target of the operation does
	/// not allow it.
	NotAllowed,
	/// This error is returned whenever an operation may fail because it times
	/// out.
	TimedOut,
	/// This leftover category is for any other error.
	///
	/// Sometimes a lower level system error is not properly mapped to a higher
	/// level error. This might be corrected in future versions of the crate.
	Other,
}

impl ErrorKind {
	pub(crate) fn as_str(&self) -> &'static str {
		match *self {
			ErrorKind::Interrupted => "interrupted",
			ErrorKind::InvalidData => "invalid data",
			ErrorKind::InvalidInput => "invalid input parameter",
			ErrorKind::NotFound => "entity not found",
			ErrorKind::NotAllowed => "operation not allowed",
			ErrorKind::TimedOut => "operation timed out",
			ErrorKind::Other => "other os error",
		}
	}
}

impl From<ErrorKind> for HaikuError {
	/// This is a shortcut to create a simple error based on an `ErrorKind`.
	fn from(kind: ErrorKind) -> HaikuError {
		HaikuError {
			repr: Repr::Simple(kind)
		}
	}
}

impl HaikuError {
	/// Create a new error with a `kind`, and a custom payload. The most
	/// common use is to attach a `String` that describes the error, but any
	/// struct that implements the `std::error::Error` trait will work.
	pub fn new<E>(kind: ErrorKind, error: E) -> HaikuError
		where E: Into<Box<dyn error::Error+Send+Sync>>
	{
		Self::_new(kind, error.into())
	}

	fn _new(kind: ErrorKind, error: Box<dyn error::Error+Send+Sync>) -> HaikuError
	{
		HaikuError {
			repr: Repr::Custom(Box::new(Custom {
				kind,
				error,
			}))
		}
	}

	/// Create a new error based on the last OS Error.
	///
	/// This function can be used to create an error after calling OS functions
	/// that set the global error number on failure.
	pub fn last_os_error() -> HaikuError {
		// Get the last OS Error
		extern {
			fn _errnop() -> *mut c_int;
		}
		let error = unsafe { *_errnop() as i32 };
		HaikuError::from_raw_os_error(error)
	}

	/// Convert a raw error constant to a `HaikuError` object
	pub fn from_raw_os_error(code: status_t) -> HaikuError {
		HaikuError { repr: Repr::Os(code) }
	}

	/// Convert the current error into a (lower level) Haiku error constant
	pub fn raw_os_error(&self) -> Option<status_t> {
		match self.repr {
			Repr::Os(i) => Some(i),
			Repr::Simple(..) => None,
			Repr::Custom(_) => None
		}
	}

	/// Get the `ErrorKind` for the current error
	pub fn kind(&self) -> ErrorKind {
		match self.repr {
			Repr::Os(e) => decode_error_kind(e),
			Repr::Simple(e) => e,
			Repr::Custom(ref e) => e.kind
		}
	}
}

impl fmt::Debug for Repr {
	fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
		match *self {
			Repr::Os(code) => 
				fmt.debug_struct("Os")
					.field("code", &code)
					.field("kind", &ErrorKind::Other)
					.field("message", &"message").finish(),
			Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(),
			Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt),
		}
	}
}

impl fmt::Display for HaikuError {
	fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
		match self.repr {
			Repr::Os(code) => {
				let detail = error_string(code);
				write!(fmt, "{} (os error {})", detail, code)
			}
			Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()),
			Repr::Custom(ref c) => c.error.fmt(fmt),
		}
	}
}

impl error::Error for HaikuError {
	fn description(&self) -> &str {
		self.kind().as_str()
	}
}

// Shamelessly taken from libstd/sys/unix/os.rs
fn error_string(errno: status_t) -> String {
	extern {
		fn strerror_r(errnum: c_int, buf: *mut c_char,
		              buflen: size_t) -> c_int;
	}
	
	let mut buf = [0 as c_char; 128];
	
	let p = buf.as_mut_ptr();
	unsafe {
		if strerror_r(errno as c_int, p, buf.len()) < 0 {
			panic!("strerror_r failure");
		}
		
		let p = p as *const _;
		str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned()
	}
}

fn decode_error_kind(errno: status_t) -> ErrorKind {
	match errno {
		B_BAD_INDEX => ErrorKind::InvalidInput,
		B_BAD_TYPE => ErrorKind::InvalidInput,
		B_BAD_VALUE => ErrorKind::InvalidInput,
		B_INTERRUPTED => ErrorKind::Interrupted,
		B_MISMATCHED_VALUES => ErrorKind::InvalidInput,
		B_NAME_NOT_FOUND => ErrorKind::NotFound,
		B_NAME_IN_USE => ErrorKind::InvalidInput,
		B_BAD_DATA => ErrorKind::InvalidData,
		B_DONT_DO_THAT => ErrorKind::InvalidInput,
		B_NOT_ALLOWED => ErrorKind::NotAllowed,
		B_TIMED_OUT => ErrorKind::TimedOut,
		_ => ErrorKind::Other
	}
}