Browse Source

Less panics, more errors

pull/137/head
Andrea Ciliberti 1 year ago
parent
commit
d71774b6bf
  1. 74
      ctru-rs/src/console.rs
  2. 4
      ctru-rs/src/lib.rs
  3. 32
      ctru-rs/src/services/hid.rs
  4. 58
      ctru-rs/src/services/ndsp/mod.rs
  5. 12
      ctru-rs/src/services/ndsp/wave.rs
  6. 14
      ctru-rs/src/services/romfs.rs
  7. 2
      ctru-sys/build.rs

74
ctru-rs/src/console.rs

@ -14,6 +14,31 @@ use crate::services::gfx::Screen;
static mut EMPTY_CONSOLE: PrintConsole = unsafe { const_zero::const_zero!(PrintConsole) }; static mut EMPTY_CONSOLE: PrintConsole = unsafe { const_zero::const_zero!(PrintConsole) };
/// Error enum for generic errors within [`Console`].
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Error {
/// The coordinate specified on the given axis exceeds the limits imposed by the [`Console`] window.
CoordinateOutOfBounds(Axis),
/// The size specified for the given dimension exceeds the limits imposed by the [`Console`] window.
DimensionOutOfBounds(Dimension),
}
/// 2D coordinate axes.
#[allow(missing_docs)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Axis {
X,
Y,
}
/// 2D dimensions.
#[allow(missing_docs)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Dimension {
Width,
Height,
}
/// Virtual text console. /// Virtual text console.
/// ///
/// [`Console`] lets the application redirect `stdout` and `stderr` to a simple text displayer on the 3DS screen. /// [`Console`] lets the application redirect `stdout` and `stderr` to a simple text displayer on the 3DS screen.
@ -174,10 +199,6 @@ impl<'screen> Console<'screen> {
/// of the new window based on the row/column coordinates of a full-screen console. /// of the new window based on the row/column coordinates of a full-screen console.
/// The second pair is the new width and height. /// The second pair is the new width and height.
/// ///
/// # Panics
///
/// This function will panic if the new window's position or size does not fit the screen.
///
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
@ -198,22 +219,22 @@ impl<'screen> Console<'screen> {
/// # } /// # }
/// ``` /// ```
#[doc(alias = "consoleSetWindow")] #[doc(alias = "consoleSetWindow")]
pub fn set_window(&mut self, x: u8, y: u8, width: u8, height: u8) { pub fn set_window(&mut self, x: u8, y: u8, width: u8, height: u8) -> Result<(), Error> {
let height_limit = 30; let height_limit = 30;
let length_limit = self.max_width(); let length_limit = self.max_width();
if x >= length_limit { if x >= length_limit {
panic!("x coordinate of new console window out of bounds"); return Err(Error::CoordinateOutOfBounds(Axis::X));
} }
if y >= height_limit { if y >= height_limit {
panic!("y coordinate of new console window out of bounds"); return Err(Error::CoordinateOutOfBounds(Axis::Y));
} }
if (x + width) > length_limit { if (x + width) > length_limit {
panic!("width of new console window out of bounds"); return Err(Error::DimensionOutOfBounds(Dimension::Width));
} }
if (y + height) > height_limit { if (y + height) > height_limit {
panic!("height of new console window out of bounds"); return Err(Error::DimensionOutOfBounds(Dimension::Height));
} }
unsafe { unsafe {
@ -225,6 +246,8 @@ impl<'screen> Console<'screen> {
height.into(), height.into(),
) )
}; };
Ok(())
} }
/// Reset the window's size to default parameters. /// Reset the window's size to default parameters.
@ -321,3 +344,36 @@ impl Drop for Console<'_> {
} }
} }
} }
impl std::fmt::Display for Axis {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::X => write!(f, "x"),
Self::Y => write!(f, "y"),
}
}
}
impl std::fmt::Display for Dimension {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Width => write!(f, "width"),
Self::Height => write!(f, "height"),
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::CoordinateOutOfBounds(a) => {
write!(f, "coordinate specified for the {a} axis is out of bounds")
}
Self::DimensionOutOfBounds(d) => {
write!(f, "size specified for the {d} is out of bounds")
}
}
}
}
impl std::error::Error for Error {}

4
ctru-rs/src/lib.rs

@ -73,7 +73,7 @@ pub fn use_panic_handler() {
/// When `test` is enabled, this function will be ignored. /// When `test` is enabled, this function will be ignored.
#[cfg(not(test))] #[cfg(not(test))]
fn panic_hook_setup() { fn panic_hook_setup() {
use crate::services::hid::{Hid, KeyPad}; use crate::services::hid::KeyPad;
use std::panic::PanicInfo; use std::panic::PanicInfo;
let main_thread = std::thread::current().id(); let main_thread = std::thread::current().id();
@ -103,7 +103,7 @@ fn panic_hook_setup() {
break; break;
} }
} }
ctru_sys::hidExit(); ctru_sys::hidExit();
} }
} }

32
ctru-rs/src/services/hid.rs

@ -80,6 +80,15 @@ bitflags! {
} }
} }
/// Error enum for generic errors within the [`Hid`] service.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Error {
/// An attempt was made to access the accelerometer while disabled.
UnavailableAccelerometer,
/// An attempt was made to access the gyroscope while disabled.
UnavailableGyroscope,
}
/// Handle to the HID service. /// Handle to the HID service.
pub struct Hid { pub struct Hid {
active_accellerometer: bool, active_accellerometer: bool,
@ -398,9 +407,9 @@ impl Hid {
} }
#[doc(alias = "hidAccelRead")] #[doc(alias = "hidAccelRead")]
pub fn accellerometer_vector(&self) -> (i16, i16, i16) { pub fn accellerometer_vector(&self) -> Result<(i16, i16, i16), Error> {
if !self.active_accellerometer { if !self.active_accellerometer {
panic!("tried to read accellerometer while disabled") return Err(Error::UnavailableAccelerometer);
} }
let mut res = ctru_sys::accelVector { x: 0, y: 0, z: 0 }; let mut res = ctru_sys::accelVector { x: 0, y: 0, z: 0 };
@ -409,13 +418,13 @@ impl Hid {
ctru_sys::hidAccelRead(&mut res); ctru_sys::hidAccelRead(&mut res);
} }
(res.x, res.y, res.z) Ok((res.x, res.y, res.z))
} }
#[doc(alias = "hidGyroRead")] #[doc(alias = "hidGyroRead")]
pub fn gyroscope_rate(&self) -> (i16, i16, i16) { pub fn gyroscope_rate(&self) -> Result<(i16, i16, i16), Error> {
if !self.active_gyroscope { if !self.active_gyroscope {
panic!("tried to read accellerometer while disabled") return Err(Error::UnavailableGyroscope);
} }
let mut res = ctru_sys::angularRate { x: 0, y: 0, z: 0 }; let mut res = ctru_sys::angularRate { x: 0, y: 0, z: 0 };
@ -424,6 +433,17 @@ impl Hid {
ctru_sys::hidGyroRead(&mut res); ctru_sys::hidGyroRead(&mut res);
} }
(res.x, res.y, res.z) Ok((res.x, res.y, res.z))
} }
} }
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::UnavailableAccelerometer => write!(f, "tried using accelerometer while disabled"),
Self::UnavailableGyroscope => write!(f, "tried using gyroscope while disabled"),
}
}
}
impl std::error::Error for Error {}

58
ctru-rs/src/services/ndsp/mod.rs

@ -52,6 +52,16 @@ pub struct AudioMix {
raw: [f32; 12], raw: [f32; 12],
} }
/// Auxiliary Device index.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(usize)]
pub enum AuxDevice {
/// Aux device with index 0.
Zero = 0,
/// Aux device with index 1.
One = 1,
}
/// Interpolation used between audio frames. /// Interpolation used between audio frames.
#[doc(alias = "ndspInterpType")] #[doc(alias = "ndspInterpType")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -67,7 +77,7 @@ pub enum InterpolationType {
/// Errors returned by [`ndsp`](self) functions. /// Errors returned by [`ndsp`](self) functions.
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum NdspError { pub enum Error {
/// Channel with the specified ID does not exist. /// Channel with the specified ID does not exist.
InvalidChannel(u8), InvalidChannel(u8),
/// Channel with the specified ID is already being used. /// Channel with the specified ID is already being used.
@ -169,7 +179,7 @@ impl Ndsp {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn channel(&self, id: u8) -> std::result::Result<Channel, NdspError> { pub fn channel(&self, id: u8) -> std::result::Result<Channel, Error> {
let in_bounds = self.channel_flags.get(id as usize); let in_bounds = self.channel_flags.get(id as usize);
match in_bounds { match in_bounds {
@ -177,10 +187,10 @@ impl Ndsp {
let flag = ref_cell.try_borrow_mut(); let flag = ref_cell.try_borrow_mut();
match flag { match flag {
Ok(_rf) => Ok(Channel { id, _rf }), Ok(_rf) => Ok(Channel { id, _rf }),
Err(_) => Err(NdspError::ChannelAlreadyInUse(id)), Err(_) => Err(Error::ChannelAlreadyInUse(id)),
} }
} }
None => Err(NdspError::InvalidChannel(id)), None => Err(Error::InvalidChannel(id)),
} }
} }
@ -516,9 +526,9 @@ impl Channel<'_> {
// TODO: Find a better way to handle the wave lifetime problem. // TODO: Find a better way to handle the wave lifetime problem.
// These "alive wave" shenanigans are the most substantial reason why I'd like to fully re-write this service in Rust. // These "alive wave" shenanigans are the most substantial reason why I'd like to fully re-write this service in Rust.
#[doc(alias = "ndspChnWaveBufAdd")] #[doc(alias = "ndspChnWaveBufAdd")]
pub fn queue_wave(&mut self, wave: &mut Wave) -> std::result::Result<(), NdspError> { pub fn queue_wave(&mut self, wave: &mut Wave) -> std::result::Result<(), Error> {
match wave.status() { match wave.status() {
Status::Playing | Status::Queued => return Err(NdspError::WaveBusy(self.id)), Status::Playing | Status::Queued => return Err(Error::WaveBusy(self.id)),
_ => (), _ => (),
} }
@ -660,23 +670,15 @@ impl AudioMix {
} }
/// Returns the values set for the "front" volume mix (left and right channel) for the specified auxiliary output device (either 0 or 1). /// Returns the values set for the "front" volume mix (left and right channel) for the specified auxiliary output device (either 0 or 1).
pub fn aux_front(&self, id: usize) -> (f32, f32) { pub fn aux_front(&self, id: AuxDevice) -> (f32, f32) {
if id > 1 { let index = 4 + (id as usize * 4);
panic!("invalid auxiliary output device index")
}
let index = 4 + id * 4;
(self.raw[index], self.raw[index + 1]) (self.raw[index], self.raw[index + 1])
} }
/// Returns the values set for the "back" volume mix (left and right channel) for the specified auxiliary output device (either 0 or 1). /// Returns the values set for the "back" volume mix (left and right channel) for the specified auxiliary output device (either 0 or 1).
pub fn aux_back(&self, id: usize) -> (f32, f32) { pub fn aux_back(&self, id: AuxDevice) -> (f32, f32) {
if id > 1 { let index = 6 + (id as usize * 4);
panic!("invalid auxiliary output device index")
}
let index = 6 + id * 4;
(self.raw[index], self.raw[index + 1]) (self.raw[index], self.raw[index + 1])
} }
@ -709,12 +711,8 @@ impl AudioMix {
/// ///
/// [`Channel`] will normalize the mix values to be within 0 and 1. /// [`Channel`] will normalize the mix values to be within 0 and 1.
/// However, an [`AudioMix`] instance with larger/smaller values is valid. /// However, an [`AudioMix`] instance with larger/smaller values is valid.
pub fn set_aux_front(&mut self, left: f32, right: f32, id: usize) { pub fn set_aux_front(&mut self, left: f32, right: f32, id: AuxDevice) {
if id > 1 { let index = 4 + (id as usize * 4);
panic!("invalid auxiliary output device index")
}
let index = 4 + id * 4;
self.raw[index] = left; self.raw[index] = left;
self.raw[index + 1] = right; self.raw[index + 1] = right;
@ -726,12 +724,8 @@ impl AudioMix {
/// ///
/// [`Channel`] will normalize the mix values to be within 0 and 1. /// [`Channel`] will normalize the mix values to be within 0 and 1.
/// However, an [`AudioMix`] instance with larger/smaller values is valid. /// However, an [`AudioMix`] instance with larger/smaller values is valid.
pub fn set_aux_back(&mut self, left: f32, right: f32, id: usize) { pub fn set_aux_back(&mut self, left: f32, right: f32, id: AuxDevice) {
if id > 1 { let index = 6 + (id as usize * 4);
panic!("invalid auxiliary output device index")
}
let index = 6 + id * 4;
self.raw[index] = left; self.raw[index] = left;
self.raw[index + 1] = right; self.raw[index + 1] = right;
@ -754,7 +748,7 @@ impl From<[f32; 12]> for AudioMix {
} }
} }
impl fmt::Display for NdspError { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Self::InvalidChannel(id) => write!(f, "audio Channel with ID {id} doesn't exist. Valid channels have an ID between 0 and 23"), Self::InvalidChannel(id) => write!(f, "audio Channel with ID {id} doesn't exist. Valid channels have an ID between 0 and 23"),
@ -765,7 +759,7 @@ impl fmt::Display for NdspError {
} }
} }
impl error::Error for NdspError {} impl error::Error for Error {}
impl Drop for Ndsp { impl Drop for Ndsp {
#[doc(alias = "ndspExit")] #[doc(alias = "ndspExit")]

12
ctru-rs/src/services/ndsp/wave.rs

@ -2,7 +2,7 @@
//! //!
//! This modules has all methods and structs required to work with audio waves meant to be played via the [`ndsp`](crate::services::ndsp) service. //! This modules has all methods and structs required to work with audio waves meant to be played via the [`ndsp`](crate::services::ndsp) service.
use super::{AudioFormat, NdspError}; use super::{AudioFormat, Error};
use crate::linear::LinearAllocator; use crate::linear::LinearAllocator;
/// Informational struct holding the raw audio data and playback info. /// Informational struct holding the raw audio data and playback info.
@ -97,10 +97,10 @@ impl Wave {
/// ///
/// This function will return an error if the [`Wave`] is currently busy, /// This function will return an error if the [`Wave`] is currently busy,
/// with the id to the channel in which it's queued. /// with the id to the channel in which it's queued.
pub fn get_buffer_mut(&mut self) -> Result<&mut [u8], NdspError> { pub fn get_buffer_mut(&mut self) -> Result<&mut [u8], Error> {
match self.status() { match self.status() {
Status::Playing | Status::Queued => { Status::Playing | Status::Queued => {
Err(NdspError::WaveBusy(self.played_on_channel.unwrap())) Err(Error::WaveBusy(self.played_on_channel.unwrap()))
} }
_ => Ok(&mut self.buffer), _ => Ok(&mut self.buffer),
} }
@ -163,10 +163,10 @@ impl Wave {
/// ///
/// This function will return an error if the sample size exceeds the buffer's capacity /// This function will return an error if the sample size exceeds the buffer's capacity
/// or if the [`Wave`] is currently queued. /// or if the [`Wave`] is currently queued.
pub fn set_sample_count(&mut self, sample_count: usize) -> Result<(), NdspError> { pub fn set_sample_count(&mut self, sample_count: usize) -> Result<(), Error> {
match self.status() { match self.status() {
Status::Playing | Status::Queued => { Status::Playing | Status::Queued => {
return Err(NdspError::WaveBusy(self.played_on_channel.unwrap())); return Err(Error::WaveBusy(self.played_on_channel.unwrap()));
} }
_ => (), _ => (),
} }
@ -174,7 +174,7 @@ impl Wave {
let max_count = self.buffer.len() / self.audio_format.size(); let max_count = self.buffer.len() / self.audio_format.size();
if sample_count > max_count { if sample_count > max_count {
return Err(NdspError::SampleCountOutOfBounds(sample_count, max_count)); return Err(Error::SampleCountOutOfBounds(sample_count, max_count));
} }
self.raw_data.nsamples = sample_count as u32; self.raw_data.nsamples = sample_count as u32;

14
ctru-rs/src/services/romfs.rs

@ -83,16 +83,12 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn romfs_counter() { #[should_panic]
let _romfs = RomFS::new().unwrap(); fn romfs_lock() {
let value = *ROMFS_ACTIVE.lock().unwrap(); let romfs = RomFS::new().unwrap();
assert_eq!(value, 1); let _value = *ROMFS_ACTIVE.lock().unwrap();
drop(_romfs); drop(romfs);
let value = *ROMFS_ACTIVE.lock().unwrap();
assert_eq!(value, 0);
} }
} }

2
ctru-sys/build.rs

@ -169,7 +169,7 @@ fn check_libctru_version() -> Result<(String, String, String), Box<dyn Error>> {
.expect("crate version should have '+' delimeter"); .expect("crate version should have '+' delimeter");
if lib_version != crate_built_version { if lib_version != crate_built_version {
return Err(format!( Err(format!(
"libctru version is {lib_version} but this crate was built for {crate_built_version}" "libctru version is {lib_version} but this crate was built for {crate_built_version}"
))?; ))?;
} }

Loading…
Cancel
Save