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. 2
      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; @@ -14,6 +14,31 @@ use crate::services::gfx::Screen;
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.
///
/// [`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> { @@ -174,10 +199,6 @@ impl<'screen> Console<'screen> {
/// of the new window based on the row/column coordinates of a full-screen console.
/// 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
///
/// ```no_run
@ -198,22 +219,22 @@ impl<'screen> Console<'screen> { @@ -198,22 +219,22 @@ impl<'screen> Console<'screen> {
/// # }
/// ```
#[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 length_limit = self.max_width();
if x >= length_limit {
panic!("x coordinate of new console window out of bounds");
return Err(Error::CoordinateOutOfBounds(Axis::X));
}
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 {
panic!("width of new console window out of bounds");
return Err(Error::DimensionOutOfBounds(Dimension::Width));
}
if (y + height) > height_limit {
panic!("height of new console window out of bounds");
return Err(Error::DimensionOutOfBounds(Dimension::Height));
}
unsafe {
@ -225,6 +246,8 @@ impl<'screen> Console<'screen> { @@ -225,6 +246,8 @@ impl<'screen> Console<'screen> {
height.into(),
)
};
Ok(())
}
/// Reset the window's size to default parameters.
@ -321,3 +344,36 @@ impl Drop for Console<'_> { @@ -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 {}

2
ctru-rs/src/lib.rs

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

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

@ -80,6 +80,15 @@ bitflags! { @@ -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.
pub struct Hid {
active_accellerometer: bool,
@ -398,9 +407,9 @@ impl Hid { @@ -398,9 +407,9 @@ impl Hid {
}
#[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 {
panic!("tried to read accellerometer while disabled")
return Err(Error::UnavailableAccelerometer);
}
let mut res = ctru_sys::accelVector { x: 0, y: 0, z: 0 };
@ -409,13 +418,13 @@ impl Hid { @@ -409,13 +418,13 @@ impl Hid {
ctru_sys::hidAccelRead(&mut res);
}
(res.x, res.y, res.z)
Ok((res.x, res.y, res.z))
}
#[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 {
panic!("tried to read accellerometer while disabled")
return Err(Error::UnavailableGyroscope);
}
let mut res = ctru_sys::angularRate { x: 0, y: 0, z: 0 };
@ -424,6 +433,17 @@ impl Hid { @@ -424,6 +433,17 @@ impl Hid {
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 { @@ -52,6 +52,16 @@ pub struct AudioMix {
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.
#[doc(alias = "ndspInterpType")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -67,7 +77,7 @@ pub enum InterpolationType { @@ -67,7 +77,7 @@ pub enum InterpolationType {
/// Errors returned by [`ndsp`](self) functions.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum NdspError {
pub enum Error {
/// Channel with the specified ID does not exist.
InvalidChannel(u8),
/// Channel with the specified ID is already being used.
@ -169,7 +179,7 @@ impl Ndsp { @@ -169,7 +179,7 @@ impl Ndsp {
/// # 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);
match in_bounds {
@ -177,10 +187,10 @@ impl Ndsp { @@ -177,10 +187,10 @@ impl Ndsp {
let flag = ref_cell.try_borrow_mut();
match flag {
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<'_> { @@ -516,9 +526,9 @@ impl Channel<'_> {
// 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.
#[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() {
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 { @@ -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).
pub fn aux_front(&self, id: usize) -> (f32, f32) {
if id > 1 {
panic!("invalid auxiliary output device index")
}
let index = 4 + id * 4;
pub fn aux_front(&self, id: AuxDevice) -> (f32, f32) {
let index = 4 + (id as usize * 4);
(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).
pub fn aux_back(&self, id: usize) -> (f32, f32) {
if id > 1 {
panic!("invalid auxiliary output device index")
}
let index = 6 + id * 4;
pub fn aux_back(&self, id: AuxDevice) -> (f32, f32) {
let index = 6 + (id as usize * 4);
(self.raw[index], self.raw[index + 1])
}
@ -709,12 +711,8 @@ impl AudioMix { @@ -709,12 +711,8 @@ impl AudioMix {
///
/// [`Channel`] will normalize the mix values to be within 0 and 1.
/// However, an [`AudioMix`] instance with larger/smaller values is valid.
pub fn set_aux_front(&mut self, left: f32, right: f32, id: usize) {
if id > 1 {
panic!("invalid auxiliary output device index")
}
let index = 4 + id * 4;
pub fn set_aux_front(&mut self, left: f32, right: f32, id: AuxDevice) {
let index = 4 + (id as usize * 4);
self.raw[index] = left;
self.raw[index + 1] = right;
@ -726,12 +724,8 @@ impl AudioMix { @@ -726,12 +724,8 @@ impl AudioMix {
///
/// [`Channel`] will normalize the mix values to be within 0 and 1.
/// However, an [`AudioMix`] instance with larger/smaller values is valid.
pub fn set_aux_back(&mut self, left: f32, right: f32, id: usize) {
if id > 1 {
panic!("invalid auxiliary output device index")
}
let index = 6 + id * 4;
pub fn set_aux_back(&mut self, left: f32, right: f32, id: AuxDevice) {
let index = 6 + (id as usize * 4);
self.raw[index] = left;
self.raw[index + 1] = right;
@ -754,7 +748,7 @@ impl From<[f32; 12]> for AudioMix { @@ -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 {
match self {
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 { @@ -765,7 +759,7 @@ impl fmt::Display for NdspError {
}
}
impl error::Error for NdspError {}
impl error::Error for Error {}
impl Drop for Ndsp {
#[doc(alias = "ndspExit")]

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

@ -2,7 +2,7 @@ @@ -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.
use super::{AudioFormat, NdspError};
use super::{AudioFormat, Error};
use crate::linear::LinearAllocator;
/// Informational struct holding the raw audio data and playback info.
@ -97,10 +97,10 @@ impl Wave { @@ -97,10 +97,10 @@ impl Wave {
///
/// This function will return an error if the [`Wave`] is currently busy,
/// 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() {
Status::Playing | Status::Queued => {
Err(NdspError::WaveBusy(self.played_on_channel.unwrap()))
Err(Error::WaveBusy(self.played_on_channel.unwrap()))
}
_ => Ok(&mut self.buffer),
}
@ -163,10 +163,10 @@ impl Wave { @@ -163,10 +163,10 @@ impl Wave {
///
/// This function will return an error if the sample size exceeds the buffer's capacity
/// 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() {
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 { @@ -174,7 +174,7 @@ impl Wave {
let max_count = self.buffer.len() / self.audio_format.size();
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;

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

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

2
ctru-sys/build.rs

@ -169,7 +169,7 @@ fn check_libctru_version() -> Result<(String, String, String), Box<dyn Error>> { @@ -169,7 +169,7 @@ fn check_libctru_version() -> Result<(String, String, String), Box<dyn Error>> {
.expect("crate version should have '+' delimeter");
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}"
))?;
}

Loading…
Cancel
Save