playdate_sys/sys/error/
null.rs

1use core::fmt;
2
3
4#[derive(Debug, PartialEq, Clone, Copy)]
5pub struct NullPtrError;
6
7impl core::error::Error for NullPtrError {
8	// Removed text from str to do not store this in output binary.
9	/// `description()` is deprecated; use `Display`
10	fn description(&self) -> &str { "" }
11}
12
13impl fmt::Display for NullPtrError {
14	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NullPtr") }
15}
16
17
18pub trait OkOrNullErr<T> {
19	fn ok_or_null<'a>(self) -> Result<&'a T, NullPtrError>;
20}
21
22impl<T> OkOrNullErr<T> for *const T {
23	fn ok_or_null<'a>(self) -> Result<&'a T, NullPtrError> { unsafe { self.as_ref() }.ok_or(NullPtrError) }
24}
25
26
27pub trait OkMutOrNullErr<T> {
28	fn ok_or_null<'a>(self) -> Result<&'a mut T, NullPtrError>;
29}
30
31impl<T> OkMutOrNullErr<T> for *mut T {
32	fn ok_or_null<'a>(self) -> Result<&'a mut T, NullPtrError> { unsafe { self.as_mut() }.ok_or(NullPtrError) }
33}
34
35
36pub trait OkOrNullFnErr<T> {
37	type Error: core::error::Error;
38	fn ok_or_null(self) -> Result<T, Self::Error>;
39}
40
41impl OkOrNullFnErr<&'static crate::ffi::PlaydateAPI> for crate::ApiRef {
42	type Error = NullPtrError;
43	fn ok_or_null(self) -> Result<&'static crate::ffi::PlaydateAPI, NullPtrError> { self.ok_or(NullPtrError) }
44}
45
46
47macro_rules! impl_fn_def {
48	($($t:ident),*) => {
49		unsafe extern "C" fn($($t),*) -> R
50	};
51
52	($($t:ident),*, ...) => {
53		unsafe extern "C" fn($($t),* ,...) -> R
54	}
55}
56
57macro_rules! impl_ok_or_null_fn_err {
58	($($t:ident),*) => {
59		impl<R, $($t),*> OkOrNullFnErr<impl_fn_def!($($t),*)> for Option<impl_fn_def!($($t),*)> {
60			type Error = NullPtrError;
61			fn ok_or_null(self) -> Result<impl_fn_def!($($t),*), NullPtrError> {
62				self.ok_or(NullPtrError)
63			}
64		}
65
66		impl<'f, R, $($t),*> OkOrNullFnErr<&'f impl_fn_def!($($t),*)> for Option<&'f impl_fn_def!($($t),*)> {
67			type Error = NullPtrError;
68			fn ok_or_null(self) -> Result<&'f impl_fn_def!($($t),*), NullPtrError> {
69				self.ok_or(NullPtrError)
70			}
71		}
72	};
73
74	($($t:ident),* ...) => {
75		impl_ok_or_null_fn_err!($($t),*);
76
77		impl<R, $($t),*> OkOrNullFnErr<impl_fn_def!($($t),* ,...)> for Option<impl_fn_def!($($t),* ,...)> {
78			type Error = NullPtrError;
79			fn ok_or_null(self) -> Result<impl_fn_def!($($t),* ,...), NullPtrError> {
80				self.ok_or(NullPtrError)
81			}
82		}
83
84		impl<'f, R, $($t),*> OkOrNullFnErr<&'f impl_fn_def!($($t),* ,...)> for Option<&'f impl_fn_def!($($t),* ,...)> {
85			type Error = NullPtrError;
86			fn ok_or_null(self) -> Result<&'f impl_fn_def!($($t),* ,...), NullPtrError> {
87				self.ok_or(NullPtrError)
88			}
89		}
90	};
91}
92
93
94impl_ok_or_null_fn_err!();
95impl_ok_or_null_fn_err!(A ...);
96impl_ok_or_null_fn_err!(A, B ...);
97impl_ok_or_null_fn_err!(A, B, C ...);
98impl_ok_or_null_fn_err!(A, B, C, D ...);
99impl_ok_or_null_fn_err!(A, B, C, D, E ...);
100impl_ok_or_null_fn_err!(A, B, C, D, E, F ...);
101impl_ok_or_null_fn_err!(A, B, C, D, E, F, G ...);
102impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H ...);
103impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I ...);
104impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J ...);
105impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J, K ...);
106impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J, K, L ...);
107impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J, K, L, M ...);
108impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J, K, L, M, N ...);
109impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O ...);
110
111
112#[cfg(test)]
113mod tests {
114	use super::*;
115
116
117	// this just should compile
118	fn api_access_ok_() -> Result<(), NullPtrError> {
119		crate::sys::api().ok_or_null()?.file.ok_or_null()?;
120		crate::sys::api().ok_or_null()?
121		                 .file
122		                 .ok_or_null()?
123		                 .open
124		                 .ok_or_null()?;
125		Ok(())
126	}
127
128	// this just should compile
129	fn api_access_raw_() -> Result<(), NullPtrError> {
130		unsafe { crate::sys::API }.ok_or_null()?.file.ok_or_null()?;
131		unsafe { crate::sys::API }.ok_or_null()?
132		                          .file
133		                          .ok_or_null()?
134		                          .open
135		                          .ok_or_null()?;
136		Ok(())
137	}
138
139	#[test]
140	fn api_access_ok() { let _ = api_access_ok_; }
141
142	#[test]
143	fn api_access_raw() { let _ = api_access_raw_; }
144}
145
146
147#[cfg(feature = "error-ctx")]
148pub mod ctx {
149	use core::fmt;
150
151
152	#[derive(Debug)]
153	pub struct NullPtrError {
154		pub ctx: &'static str,
155	}
156
157	impl fmt::Display for NullPtrError {
158		fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Ptr to {} is null", self.ctx) }
159	}
160
161	impl Into<super::NullPtrError> for NullPtrError {
162		/// Convert to non-contextual error.
163		/// Removes context of this error.
164		fn into(self) -> super::NullPtrError { super::NullPtrError }
165	}
166
167	impl core::error::Error for NullPtrError {
168		// Removed text from str to do not store this in output binary.
169		/// `description()` is deprecated; use `Display`
170		fn description(&self) -> &str { "" }
171	}
172
173	impl AsRef<str> for NullPtrError {
174		fn as_ref(&self) -> &str { self.ctx }
175	}
176
177
178	pub trait OkOrNullCtx<T> {
179		fn ok_or_null_ctx<'a>(self, ctx: &'static str) -> Result<&'a T, NullPtrError>;
180	}
181
182	impl<T> OkOrNullCtx<T> for *const T {
183		fn ok_or_null_ctx<'a>(self, ctx: &'static str) -> Result<&'a T, NullPtrError> {
184			unsafe { self.as_ref() }.ok_or(NullPtrError { ctx })
185		}
186	}
187
188
189	pub trait OkOrNullAddCtx<T> {
190		/// Convert result with non-contextual error to result with error with given `ctx`.
191		fn ctx(self, ctx: &'static str) -> Result<T, NullPtrError>;
192	}
193
194	impl<T> OkOrNullAddCtx<T> for Result<T, super::NullPtrError> {
195		fn ctx(self, ctx: &'static str) -> Result<T, NullPtrError> {
196			match self {
197				Ok(t) => Ok(t),
198				Err(_) => Err(NullPtrError { ctx }),
199			}
200		}
201	}
202
203
204	pub trait OkOrNullFnCtxErr<T> {
205		type Error: core::error::Error;
206		fn ok_or_null_ctx(self, ctx: &'static str) -> Result<T, Self::Error>;
207	}
208
209	impl OkOrNullFnCtxErr<&'static crate::ffi::PlaydateAPI> for crate::ApiRef {
210		type Error = NullPtrError;
211		fn ok_or_null_ctx(self, ctx: &'static str) -> Result<&'static crate::ffi::PlaydateAPI, NullPtrError> {
212			self.ok_or(NullPtrError { ctx })
213		}
214	}
215
216	macro_rules! impl_ok_or_null_fn_err {
217		($($t:ident),*) => {
218			impl<R, $($t),*> OkOrNullFnCtxErr<impl_fn_def!($($t),*)> for Option<impl_fn_def!($($t),*)> {
219				type Error = NullPtrError;
220				fn ok_or_null_ctx(self, ctx: &'static str) -> Result<impl_fn_def!($($t),*), NullPtrError> {
221					self.ok_or(NullPtrError { ctx })
222				}
223			}
224		};
225	}
226
227	impl_ok_or_null_fn_err!();
228	impl_ok_or_null_fn_err!(A);
229	impl_ok_or_null_fn_err!(A, B);
230	impl_ok_or_null_fn_err!(A, B, C);
231	impl_ok_or_null_fn_err!(A, B, C, D);
232	impl_ok_or_null_fn_err!(A, B, C, D, E);
233	impl_ok_or_null_fn_err!(A, B, C, D, E, F);
234	impl_ok_or_null_fn_err!(A, B, C, D, E, F, G);
235	impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H);
236	impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I);
237	impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J);
238	impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J, K);
239	impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J, K, L);
240	impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J, K, L, M);
241	impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
242	impl_ok_or_null_fn_err!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
243
244
245	#[cfg(test)]
246	mod tests {
247		use super::*;
248
249
250		// this just should compile
251		fn api_access_ok_() -> Result<(), NullPtrError> {
252			crate::sys::api().ok_or_null_ctx("api")?
253			                 .file
254			                 .ok_or_null_ctx("file")?;
255			crate::sys::api().ok_or_null_ctx("api")?
256			                 .file
257			                 .ok_or_null_ctx("file")?
258			                 .open
259			                 .ok_or_null_ctx("open")?;
260			Ok(())
261		}
262
263		// this just should compile
264		fn api_access_raw_() -> Result<(), NullPtrError> {
265			unsafe { crate::sys::API }.ok_or_null_ctx("api")?
266			                          .file
267			                          .ok_or_null_ctx("file")?;
268			unsafe { crate::sys::API }.ok_or_null_ctx("api")?
269			                          .file
270			                          .ok_or_null_ctx("file")?
271			                          .open
272			                          .ok_or_null_ctx("open")?;
273			Ok(())
274		}
275
276		#[test]
277		fn api_access_ok() { let _ = api_access_ok_; }
278
279		#[test]
280		fn api_access_raw() { let _ = api_access_raw_; }
281	}
282}