1use std::borrow::Cow;
6use std::error;
7use std::ffi::CStr;
8use std::fmt;
9use std::ops::{ControlFlow, FromResidual, Try};
10
11use ctru_sys::result::{R_DESCRIPTION, R_LEVEL, R_MODULE, R_SUMMARY};
12
13pub type Result<T> = ::std::result::Result<T, Error>;
17
18#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
38#[repr(transparent)]
39pub struct ResultCode(pub ctru_sys::Result);
40
41impl Try for ResultCode {
42 type Output = ();
43 type Residual = Error;
44
45 fn from_output(_: Self::Output) -> Self {
46 Self(0)
47 }
48
49 fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
50 if ctru_sys::R_FAILED(self.0) || ctru_sys::R_SUMMARY(self.0) != ctru_sys::RS_SUCCESS {
54 ControlFlow::Break(self.into())
55 } else {
56 ControlFlow::Continue(())
57 }
58 }
59}
60
61impl FromResidual for ResultCode {
62 fn from_residual(e: <Self as Try>::Residual) -> Self {
63 match e {
64 Error::Os(result) => Self(result),
65 _ => unreachable!(),
66 }
67 }
68}
69
70impl<T> FromResidual<Error> for Result<T> {
71 fn from_residual(e: Error) -> Self {
72 Err(e)
73 }
74}
75
76#[non_exhaustive]
80pub enum Error {
81 Os(ctru_sys::Result),
83 Libc(String),
85 ServiceAlreadyActive,
87 OutputAlreadyRedirected,
89 BufferTooShort {
91 provided: usize,
93 wanted: usize,
95 },
96 Other(String),
98}
99
100impl Error {
101 pub(crate) fn from_errno() -> Self {
105 let error_str = unsafe {
106 let errno = ctru_sys::errno();
107 let str_ptr = libc::strerror(errno);
108
109 CStr::from_ptr(str_ptr)
112 };
113
114 Self::Libc(error_str.to_string_lossy().into())
116 }
117
118 pub fn is_timeout(&self) -> bool {
120 match *self {
121 Error::Os(code) => R_DESCRIPTION(code) == ctru_sys::RD_TIMEOUT,
122 _ => false,
123 }
124 }
125}
126
127impl From<ctru_sys::Result> for Error {
128 fn from(err: ctru_sys::Result) -> Self {
129 Error::Os(err)
130 }
131}
132
133impl From<ResultCode> for Error {
134 fn from(err: ResultCode) -> Self {
135 Self::Os(err.0)
136 }
137}
138
139impl fmt::Debug for Error {
140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141 match self {
142 &Self::Os(err) => f
143 .debug_struct("Error")
144 .field("raw", &format_args!("{err:#08X}"))
145 .field("level", &result_code_level_str(err))
146 .field("module", &result_code_module_str(err))
147 .field("summary", &result_code_summary_str(err))
148 .field("description", &result_code_description_str(err))
149 .finish(),
150 Self::Libc(err) => f.debug_tuple("Libc").field(err).finish(),
151 Self::ServiceAlreadyActive => f.debug_tuple("ServiceAlreadyActive").finish(),
152 Self::OutputAlreadyRedirected => f.debug_tuple("OutputAlreadyRedirected").finish(),
153 Self::BufferTooShort { provided, wanted } => f
154 .debug_struct("BufferTooShort")
155 .field("provided", provided)
156 .field("wanted", wanted)
157 .finish(),
158 Self::Other(err) => f.debug_tuple("Other").field(err).finish(),
159 }
160 }
161}
162
163impl fmt::Display for Error {
164 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165 match self {
166 &Self::Os(err) => write!(
169 f,
170 "libctru result code 0x{err:08X}: [{} {}] {}: {}",
171 result_code_level_str(err),
172 result_code_module_str(err),
173 result_code_summary_str(err),
174 result_code_description_str(err)
175 ),
176 Self::Libc(err) => write!(f, "{err}"),
177 Self::ServiceAlreadyActive => write!(f, "service already active"),
178 Self::OutputAlreadyRedirected => {
179 write!(f, "output streams are already redirected to 3dslink")
180 }
181 Self::BufferTooShort { provided, wanted } => write!(
182 f,
183 "the provided buffer's length is too short (length = {provided}) to hold the wanted data (size = {wanted})"
184 ),
185 Self::Other(err) => write!(f, "{err}"),
186 }
187 }
188}
189
190impl error::Error for Error {}
191
192fn result_code_level_str(result: ctru_sys::Result) -> Cow<'static, str> {
193 use ctru_sys::{
194 RL_FATAL, RL_INFO, RL_PERMANENT, RL_REINITIALIZE, RL_RESET, RL_STATUS, RL_SUCCESS,
195 RL_TEMPORARY, RL_USAGE,
196 };
197
198 Cow::Borrowed(match R_LEVEL(result) {
199 RL_SUCCESS => "success",
200 RL_INFO => "info",
201 RL_FATAL => "fatal",
202 RL_RESET => "reset",
203 RL_REINITIALIZE => "reinitialize",
204 RL_USAGE => "usage",
205 RL_PERMANENT => "permanent",
206 RL_TEMPORARY => "temporary",
207 RL_STATUS => "status",
208 code => return Cow::Owned(format!("(unknown level: {code:#x})")),
209 })
210}
211
212fn result_code_summary_str(result: ctru_sys::Result) -> Cow<'static, str> {
213 use ctru_sys::{
214 RS_CANCELED, RS_INTERNAL, RS_INVALIDARG, RS_INVALIDRESVAL, RS_INVALIDSTATE, RS_NOP,
215 RS_NOTFOUND, RS_NOTSUPPORTED, RS_OUTOFRESOURCE, RS_STATUSCHANGED, RS_SUCCESS,
216 RS_WOULDBLOCK, RS_WRONGARG,
217 };
218
219 Cow::Borrowed(match R_SUMMARY(result) {
220 RS_SUCCESS => "success",
221 RS_NOP => "nop",
222 RS_WOULDBLOCK => "would_block",
223 RS_OUTOFRESOURCE => "out_of_resource",
224 RS_NOTFOUND => "not_found",
225 RS_INVALIDSTATE => "invalid_state",
226 RS_NOTSUPPORTED => "not_supported",
227 RS_INVALIDARG => "invalid_arg",
228 RS_WRONGARG => "wrong_arg",
229 RS_CANCELED => "canceled",
230 RS_STATUSCHANGED => "status_changed",
231 RS_INTERNAL => "internal",
232 RS_INVALIDRESVAL => "invalid_res_val",
233 code => return Cow::Owned(format!("(unknown summary: {code:#x})")),
234 })
235}
236
237fn result_code_description_str(result: ctru_sys::Result) -> Cow<'static, str> {
238 use ctru_sys::{
239 RD_ALREADY_DONE, RD_ALREADY_EXISTS, RD_ALREADY_INITIALIZED, RD_BUSY, RD_CANCEL_REQUESTED,
240 RD_INVALID_ADDRESS, RD_INVALID_COMBINATION, RD_INVALID_ENUM_VALUE, RD_INVALID_HANDLE,
241 RD_INVALID_POINTER, RD_INVALID_RESULT_VALUE, RD_INVALID_SELECTION, RD_INVALID_SIZE,
242 RD_MISALIGNED_ADDRESS, RD_MISALIGNED_SIZE, RD_NO_DATA, RD_NOT_AUTHORIZED, RD_NOT_FOUND,
243 RD_NOT_IMPLEMENTED, RD_NOT_INITIALIZED, RD_OUT_OF_MEMORY, RD_OUT_OF_RANGE, RD_SUCCESS,
244 RD_TIMEOUT, RD_TOO_LARGE,
245 };
246
247 Cow::Borrowed(match R_DESCRIPTION(result) {
248 RD_SUCCESS => "success",
249 RD_INVALID_RESULT_VALUE => "invalid_result_value",
250 RD_TIMEOUT => "timeout",
251 RD_OUT_OF_RANGE => "out_of_range",
252 RD_ALREADY_EXISTS => "already_exists",
253 RD_CANCEL_REQUESTED => "cancel_requested",
254 RD_NOT_FOUND => "not_found",
255 RD_ALREADY_INITIALIZED => "already_initialized",
256 RD_NOT_INITIALIZED => "not_initialized",
257 RD_INVALID_HANDLE => "invalid_handle",
258 RD_INVALID_POINTER => "invalid_pointer",
259 RD_INVALID_ADDRESS => "invalid_address",
260 RD_NOT_IMPLEMENTED => "not_implemented",
261 RD_OUT_OF_MEMORY => "out_of_memory",
262 RD_MISALIGNED_SIZE => "misaligned_size",
263 RD_MISALIGNED_ADDRESS => "misaligned_address",
264 RD_BUSY => "busy",
265 RD_NO_DATA => "no_data",
266 RD_INVALID_COMBINATION => "invalid_combination",
267 RD_INVALID_ENUM_VALUE => "invalid_enum_value",
268 RD_INVALID_SIZE => "invalid_size",
269 RD_ALREADY_DONE => "already_done",
270 RD_NOT_AUTHORIZED => "not_authorized",
271 RD_TOO_LARGE => "too_large",
272 RD_INVALID_SELECTION => "invalid_selection",
273 code => {
274 let error = unsafe { CStr::from_ptr(ctru_sys::osStrError(result)) }.to_str();
275 match error {
276 Ok(err) => err,
277 Err(_) => return Cow::Owned(format!("(unknown description: {code:#x})")),
278 }
279 }
280 })
281}
282
283fn result_code_module_str(result: ctru_sys::Result) -> Cow<'static, str> {
284 use ctru_sys::{
285 RM_AC, RM_ACC, RM_ACT, RM_AM, RM_AM_LOW, RM_APPLET, RM_APPLICATION, RM_AVD, RM_BOSS,
286 RM_CAM, RM_CARD, RM_CARD_SPI, RM_CARDNOR, RM_CEC, RM_CODEC, RM_COMMON, RM_CONFIG, RM_CSND,
287 RM_CUP, RM_DBG, RM_DBM, RM_DD, RM_DI, RM_DLP, RM_DMNT, RM_DSP, RM_EC, RM_ENC, RM_FATFS,
288 RM_FILE_SERVER, RM_FND, RM_FRIENDS, RM_FS, RM_FSI, RM_GD, RM_GPIO, RM_GSP, RM_GYROSCOPE,
289 RM_HID, RM_HIO, RM_HIO_LOW, RM_HTTP, RM_I2C, RM_INVALIDRESVAL, RM_IR, RM_KERNEL, RM_L2B,
290 RM_LDR, RM_LOADER_SERVER, RM_MC, RM_MCU, RM_MIC, RM_MIDI, RM_MP, RM_MPWL, RM_MVD, RM_NDM,
291 RM_NEIA, RM_NEWS, RM_NEX, RM_NFC, RM_NFP, RM_NGC, RM_NIM, RM_NPNS, RM_NS, RM_NWM, RM_OLV,
292 RM_OS, RM_PDN, RM_PI, RM_PIA, RM_PL, RM_PM, RM_PM_LOW, RM_PS, RM_PTM, RM_PXI, RM_QTM,
293 RM_RDT, RM_RO, RM_ROMFS, RM_SDMC, RM_SND, RM_SOC, RM_SPI, RM_SPM, RM_SRV, RM_SSL, RM_SWC,
294 RM_TCB, RM_TEST, RM_UART, RM_UDS, RM_UPDATER, RM_UTIL, RM_VCTL, RM_WEB_BROWSER,
295 };
296
297 Cow::Borrowed(match R_MODULE(result) {
298 RM_COMMON => "common",
299 RM_KERNEL => "kernel",
300 RM_UTIL => "util",
301 RM_FILE_SERVER => "file_server",
302 RM_LOADER_SERVER => "loader_server",
303 RM_TCB => "tcb",
304 RM_OS => "os",
305 RM_DBG => "dbg",
306 RM_DMNT => "dmnt",
307 RM_PDN => "pdn",
308 RM_GSP => "gsp",
309 RM_I2C => "i2c",
310 RM_GPIO => "gpio",
311 RM_DD => "dd",
312 RM_CODEC => "codec",
313 RM_SPI => "spi",
314 RM_PXI => "pxi",
315 RM_FS => "fs",
316 RM_DI => "di",
317 RM_HID => "hid",
318 RM_CAM => "cam",
319 RM_PI => "pi",
320 RM_PM => "pm",
321 RM_PM_LOW => "pm_low",
322 RM_FSI => "fsi",
323 RM_SRV => "srv",
324 RM_NDM => "ndm",
325 RM_NWM => "nwm",
326 RM_SOC => "soc",
327 RM_LDR => "ldr",
328 RM_ACC => "acc",
329 RM_ROMFS => "romfs",
330 RM_AM => "am",
331 RM_HIO => "hio",
332 RM_UPDATER => "updater",
333 RM_MIC => "mic",
334 RM_FND => "fnd",
335 RM_MP => "mp",
336 RM_MPWL => "mpwl",
337 RM_AC => "ac",
338 RM_HTTP => "http",
339 RM_DSP => "dsp",
340 RM_SND => "snd",
341 RM_DLP => "dlp",
342 RM_HIO_LOW => "hio_low",
343 RM_CSND => "csnd",
344 RM_SSL => "ssl",
345 RM_AM_LOW => "am_low",
346 RM_NEX => "nex",
347 RM_FRIENDS => "friends",
348 RM_RDT => "rdt",
349 RM_APPLET => "applet",
350 RM_NIM => "nim",
351 RM_PTM => "ptm",
352 RM_MIDI => "midi",
353 RM_MC => "mc",
354 RM_SWC => "swc",
355 RM_FATFS => "fatfs",
356 RM_NGC => "ngc",
357 RM_CARD => "card",
358 RM_CARDNOR => "cardnor",
359 RM_SDMC => "sdmc",
360 RM_BOSS => "boss",
361 RM_DBM => "dbm",
362 RM_CONFIG => "config",
363 RM_PS => "ps",
364 RM_CEC => "cec",
365 RM_IR => "ir",
366 RM_UDS => "uds",
367 RM_PL => "pl",
368 RM_CUP => "cup",
369 RM_GYROSCOPE => "gyroscope",
370 RM_MCU => "mcu",
371 RM_NS => "ns",
372 RM_NEWS => "news",
373 RM_RO => "ro",
374 RM_GD => "gd",
375 RM_CARD_SPI => "card_spi",
376 RM_EC => "ec",
377 RM_WEB_BROWSER => "web_browser",
378 RM_TEST => "test",
379 RM_ENC => "enc",
380 RM_PIA => "pia",
381 RM_ACT => "act",
382 RM_VCTL => "vctl",
383 RM_OLV => "olv",
384 RM_NEIA => "neia",
385 RM_NPNS => "npns",
386 RM_AVD => "avd",
387 RM_L2B => "l2b",
388 RM_MVD => "mvd",
389 RM_NFC => "nfc",
390 RM_UART => "uart",
391 RM_SPM => "spm",
392 RM_QTM => "qtm",
393 RM_NFP => "nfp",
394 RM_APPLICATION => "application",
395 RM_INVALIDRESVAL => "invalid_res_val",
396 code => return Cow::Owned(format!("(unknown module: {code:#x})")),
397 })
398}