From e55736344dc14892d501e396991fac6cdc7b298b Mon Sep 17 00:00:00 2001 From: G2-Games Date: Mon, 11 Mar 2024 03:57:47 -0500 Subject: [PATCH] Added proper error types --- Cargo.toml | 1 + src/lib.rs | 8 +- src/netmd/base.rs | 65 ++++--- src/netmd/commands.rs | 3 +- src/netmd/interface.rs | 380 +++++++++++++++++++++++---------------- src/netmd/query_utils.rs | 76 ++++++-- src/netmd/utils.rs | 8 +- 7 files changed, 340 insertions(+), 201 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3bf65da..bbeffcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ cbc = "0.1" ecb = "0.1" tokio = { version = "1.36", features = ["sync"] } unicode-jp = { git = "https://github.com/uzabase/unicode-jp-rs.git" } +thiserror = "1.0.57" [target.'cfg(target_family = "wasm")'.dependencies] gloo = { version = "0.11.0", features = ["futures"] } diff --git a/src/lib.rs b/src/lib.rs index 3eb0d66..0506acc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,5 @@ -/*! - * This crate is an interface in rust to control NetMD and Hi-MD minidisc devices. - * - * Documentation coming soon - */ +/// A crate for controlling NetMD and Hi-MD devices. +/// +/// To use this library, first you need to get a device from [cross-usb] and then open a [netmd::interface::NetMDInterface] pub mod netmd; diff --git a/src/netmd/base.rs b/src/netmd/base.rs index a1152e5..9f680d6 100644 --- a/src/netmd/base.rs +++ b/src/netmd/base.rs @@ -1,16 +1,16 @@ #![cfg_attr(debug_assertions, allow(dead_code))] -use nofmt; -use once_cell::sync::Lazy; -use std::error::Error; use std::time::Duration; +use nofmt; +use once_cell::sync::Lazy; +use thiserror::Error; + // USB stuff -use cross_usb::usb::{ControlIn, ControlOut, ControlType, Device, Interface, Recipient}; +use cross_usb::usb::{ControlIn, ControlOut, ControlType, Device, Interface, Recipient, UsbError}; use cross_usb::{UsbDevice, UsbInterface}; use super::utils::cross_sleep; -const DEFAULT_TIMEOUT: Duration = Duration::new(10000, 0); const BULK_WRITE_ENDPOINT: u8 = 0x02; const BULK_READ_ENDPOINT: u8 = 0x81; @@ -89,13 +89,32 @@ pub enum Status { } /// The ID of a device, including the name +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct DeviceId { vendor_id: u16, product_id: u16, name: Option, } -/// A connection to a NetMD device +#[derive(Error, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum NetMDError { + #[error("communication timed out")] + Timeout, + + #[error("invalid usb result")] + InvalidResult, + + #[error("the device is not ready")] + NotReady, + + #[error("could not find device")] + UnknownDevice(DeviceId), + + #[error("usb connection error")] + UsbError(#[from] UsbError), +} + +/// A USB connection to a NetMD device pub struct NetMD { usb_interface: UsbInterface, model: DeviceId, @@ -105,7 +124,7 @@ impl NetMD { const READ_REPLY_RETRY_INTERVAL: u32 = 10; /// Creates a new interface to a NetMD device - pub async fn new(usb_device: &UsbDevice) -> Result> { + pub async fn new(usb_device: &UsbDevice) -> Result { let mut model = DeviceId { vendor_id: usb_device.vendor_id().await, product_id: usb_device.product_id().await, @@ -122,7 +141,7 @@ impl NetMD { } match model.name { - None => return Err("Could not find device in list".into()), + None => return Err(NetMDError::UnknownDevice(model)), Some(_) => (), } @@ -151,7 +170,7 @@ impl NetMD { /// Poll the device to get either the result /// of the previous command, or the status - pub async fn poll(&mut self) -> Result<(u16, [u8; 4]), Box> { + pub async fn poll(&mut self) -> Result<(u16, [u8; 4]), NetMDError> { // Create an array to store the result of the poll let poll_result = match self .usb_interface @@ -173,17 +192,17 @@ impl NetMD { let poll_result: [u8; 4] = match poll_result.try_into() { Ok(val) => val, - Err(_) => return Err("could not convert result".into()), + Err(_) => return Err(NetMDError::InvalidResult), }; Ok((length_bytes, poll_result)) } - pub async fn send_command(&mut self, command: Vec) -> Result<(), Box> { + pub async fn send_command(&mut self, command: Vec) -> Result<(), NetMDError> { self._send_command(command, false).await } - pub async fn send_factory_command(&mut self, command: Vec) -> Result<(), Box> { + pub async fn send_factory_command(&mut self, command: Vec) -> Result<(), NetMDError> { self._send_command(command, true).await } @@ -192,12 +211,12 @@ impl NetMD { &mut self, command: Vec, use_factory_command: bool, - ) -> Result<(), Box> { + ) -> Result<(), NetMDError> { // First poll to ensure the device is ready match self.poll().await { Ok(buffer) => match buffer.1[2] { 0 => 0, - _ => return Err("Device not ready!".into()), + _ => return Err(NetMDError::NotReady), }, Err(error) => return Err(error), }; @@ -227,14 +246,14 @@ impl NetMD { pub async fn read_reply( &mut self, override_length: Option, - ) -> Result, Box> { + ) -> Result, NetMDError> { self._read_reply(false, override_length).await } pub async fn read_factory_reply( &mut self, override_length: Option, - ) -> Result, Box> { + ) -> Result, NetMDError> { self._read_reply(true, override_length).await } @@ -243,12 +262,12 @@ impl NetMD { &mut self, use_factory_command: bool, override_length: Option, - ) -> Result, Box> { + ) -> Result, NetMDError> { let mut length = 0; for attempt in 0..40 { if attempt == 39 { - return Err("Failed to get response length".into()); + return Err(NetMDError::Timeout); } length = self.poll().await?.0; @@ -260,7 +279,7 @@ impl NetMD { // Back off while trying again let sleep_time = Self::READ_REPLY_RETRY_INTERVAL * (u32::pow(2, attempt) - 1); - cross_sleep(sleep_time).await; + cross_sleep(Duration::from_millis(sleep_time as u64)).await; } if let Some(value) = override_length { @@ -293,7 +312,7 @@ impl NetMD { &mut self, length: usize, chunksize: usize, - ) -> Result, Box> { + ) -> Result, NetMDError> { let result = self.read_bulk_to_array(length, chunksize).await?; Ok(result) @@ -303,7 +322,7 @@ impl NetMD { &mut self, length: usize, chunksize: usize, - ) -> Result, Box> { + ) -> Result, NetMDError> { let mut final_result: Vec = Vec::new(); let mut done = 0; @@ -317,7 +336,7 @@ impl NetMD { .await { Ok(result) => result, - Err(error) => return Err(format!("USB error: {:?}", error).into()), + Err(error) => return Err(NetMDError::UsbError(error)), }; final_result.extend_from_slice(&res); @@ -326,7 +345,7 @@ impl NetMD { Ok(final_result) } - pub async fn write_bulk(&mut self, data: &[u8]) -> Result> { + pub async fn write_bulk(&mut self, data: &[u8]) -> Result { Ok(self .usb_interface .bulk_out(BULK_WRITE_ENDPOINT, data) diff --git a/src/netmd/commands.rs b/src/netmd/commands.rs index 14e5770..bb57ea4 100644 --- a/src/netmd/commands.rs +++ b/src/netmd/commands.rs @@ -2,6 +2,7 @@ use num_derive::FromPrimitive; use num_traits::FromPrimitive; use std::error::Error; +use std::time::Duration; use super::interface::{MDSession, MDTrack, NetMDInterface}; use super::utils::cross_sleep; @@ -69,7 +70,7 @@ pub async fn prepare_download(interface: &mut NetMDInterface) -> Result<(), Box< .state .unwrap_or(OperatingStatus::NoDisc), ) { - cross_sleep(200).await; + cross_sleep(Duration::from_millis(200)).await; } let _ = interface.session_key_forget().await; diff --git a/src/netmd/interface.rs b/src/netmd/interface.rs index b5bef92..d1da7f0 100644 --- a/src/netmd/interface.rs +++ b/src/netmd/interface.rs @@ -11,11 +11,14 @@ use encoding_rs::SHIFT_JIS; use num_derive::FromPrimitive; use rand::RngCore; use std::collections::HashMap; +use std::time::Duration; +use thiserror::Error; use std::error::Error; use tokio::sync::mpsc::UnboundedReceiver; use lazy_static::lazy_static; +use super::base::NetMD; use super::utils::cross_sleep; #[derive(Copy, Clone)] @@ -86,6 +89,7 @@ enum DiscFlag { WriteProtected = 0x40, } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] enum NetMDLevel { Level1 = 0x20, // Network MD Level2 = 0x50, // Program play MD @@ -93,14 +97,14 @@ enum NetMDLevel { } impl std::convert::TryFrom for NetMDLevel { - type Error = Box; + type Error = InterfaceError; - fn try_from(item: u8) -> Result> { + fn try_from(item: u8) -> Result { match item { 0x20 => Ok(NetMDLevel::Level1), 0x50 => Ok(NetMDLevel::Level2), 0x70 => Ok(NetMDLevel::Level3), - _ => Err("Value not valid NetMD Level".into()), + _ => Err(InterfaceError::InvalidLevel(item)), } } } @@ -166,10 +170,14 @@ lazy_static! { ]); } -impl std::convert::TryFrom for NetmdStatus { - type Error = Box; +#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[error("invalid status code")] +pub struct StatusError; - fn try_from(item: u8) -> Result> { +impl TryFrom for NetmdStatus { + type Error = StatusError; + + fn try_from(item: u8) -> Result { match item { 0x00 => Ok(NetmdStatus::Control), 0x01 => Ok(NetmdStatus::Status), @@ -183,7 +191,7 @@ impl std::convert::TryFrom for NetmdStatus { 0x0c => Ok(NetmdStatus::Implemented), 0x0d => Ok(NetmdStatus::Changed), 0x0f => Ok(NetmdStatus::Interim), - _ => Err("Not a valid value".into()), + _ => Err(StatusError), } } } @@ -196,9 +204,63 @@ struct MediaInfo { supports_md_clip: u8, } +#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub enum EncryptionError { + #[error("supplied depth of {0} is invalid")] + InvalidDepth(i32), + + #[error("supplied {0} length of {1} is invalid")] + InvalidLength(&'static str, usize), +} + +#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub enum InterfaceError { + #[error("could not parse data from a device")] + QueryError(#[from] crate::netmd::query_utils::QueryError), + + #[error("communication with the device failed")] + CommunicationError(#[from] crate::netmd::base::NetMDError), + + #[error("track {0} is in 2 groups")] + GroupError(String), + + #[error("encryption error: {0:?}")] + EncryptionError(#[from] EncryptionError), + + #[error("device status invalid: {0:?}")] + InvalidStatus(#[from] StatusError), + + #[error("no supported media types found")] + NoSupportedMedia, + + #[error("invalid NetMD level: {0}")] + InvalidLevel(u8), + + #[error("track encoding value {0} out of range (0x90..0x92)")] + InvalidEncoding(u8), + + #[error("disc format value {0} out of range (0..6)")] + InvalidDiscFormat(u8), + + #[error("the device rejected the message")] + Rejected(String), + + #[error("the title is the same as the target title")] + TitleError, + + #[error("the device replied that the command was not recognized")] + NotImplemented(String), + + #[error("the maximum number of retry attempts was reached")] + MaxRetries, + + #[error("the device replied with an unknown status")] + Unknown(String), +} + /// An interface for interacting with a NetMD device pub struct NetMDInterface { - pub net_md_device: base::NetMD, + pub net_md_device: NetMD, } #[allow(dead_code)] @@ -207,7 +269,7 @@ impl NetMDInterface { const INTERIM_RESPONSE_RETRY_INTERVAL: u32 = 100; /// Get a new interface to a NetMD device - pub async fn new(device: &cross_usb::UsbDevice) -> Result> { + pub async fn new(device: &cross_usb::UsbDevice) -> Result { let net_md_device = base::NetMD::new(device).await?; Ok(NetMDInterface { net_md_device }) } @@ -223,12 +285,11 @@ impl NetMDInterface { } // TODO: Finish proper implementation - async fn disc_subunit_identifier(&mut self) -> Result> { + async fn disc_subunit_identifier(&mut self) -> Result { self.change_descriptor_state( &Descriptor::DiscSubunitIdentifier, &DescriptorAction::OpenRead, - ) - .await; + ).await?; let mut query = format_query("1809 00 ff00 0000 0000".to_string(), vec![])?; @@ -297,7 +358,7 @@ impl NetMDInterface { &buffer[buffer_offset..buffer_offset + manufacturer_dep_length as usize]; self.change_descriptor_state(&Descriptor::DiscSubunitIdentifier, &DescriptorAction::Close) - .await; + .await?; for media in supported_media_type_specifications { if media.supported_media_type != 0x301 { @@ -306,7 +367,7 @@ impl NetMDInterface { return NetMDLevel::try_from(media.implementation_profile_id); } - Err("No supported media types found".into()) + Err(InterfaceError::NoSupportedMedia) } /* TODO: Finish implementation @@ -321,7 +382,7 @@ impl NetMDInterface { } */ - async fn net_md_level(&mut self) -> Result> { + async fn net_md_level(&mut self) -> Result { let result = self.disc_subunit_identifier().await?; Ok(result) @@ -331,7 +392,7 @@ impl NetMDInterface { &mut self, descriptor: &Descriptor, action: &DescriptorAction, - ) { + ) -> Result<(), InterfaceError> { let mut query = format_query("1808".to_string(), vec![]).unwrap(); query.append(&mut descriptor.get_array()); @@ -340,7 +401,10 @@ impl NetMDInterface { query.push(0x00); - let _ = self.send_query(&mut query, false, false).await; + match self.send_query(&mut query, false, false).await { + Ok(_) => Ok(()), + Err(err) => Err(err), + } } /// Send a query to the NetMD player @@ -349,7 +413,7 @@ impl NetMDInterface { query: &mut Vec, test: bool, accept_interim: bool, - ) -> Result, Box> { + ) -> Result, InterfaceError> { self.send_command(query, test).await?; let result = self.read_reply(accept_interim).await?; @@ -357,11 +421,7 @@ impl NetMDInterface { Ok(result) } - async fn send_command( - &mut self, - query: &mut Vec, - test: bool, - ) -> Result<(), Box> { + async fn send_command(&mut self, query: &mut Vec, test: bool) -> Result<(), InterfaceError> { let status_byte = match test { true => NetmdStatus::GeneralInquiry, false => NetmdStatus::Control, @@ -377,48 +437,44 @@ impl NetMDInterface { Ok(()) } - async fn read_reply(&mut self, accept_interim: bool) -> Result, Box> { + async fn read_reply(&mut self, accept_interim: bool) -> Result, InterfaceError> { let mut current_attempt = 0; let mut data; while current_attempt < Self::MAX_INTERIM_READ_ATTEMPTS { - data = match self.net_md_device.read_reply(None).await { - Ok(reply) => reply, - Err(error) => return Err(error), - }; + data = self.net_md_device.read_reply(None).await?; - let status = match NetmdStatus::try_from(data[0]) { - Ok(status) => status, - Err(error) => return Err(error), - }; + let status = NetmdStatus::try_from(data[0])?; match status { - NetmdStatus::NotImplemented => return Err("Not implemented".into()), - NetmdStatus::Rejected => return Err("Rejected".into()), + NetmdStatus::NotImplemented => { + return Err(InterfaceError::NotImplemented(format!("{:X?}", data))) + } + NetmdStatus::Rejected => return Err(InterfaceError::Rejected(format!("{:X?}", data))), NetmdStatus::Interim if !accept_interim => { let sleep_time = Self::INTERIM_RESPONSE_RETRY_INTERVAL * (u32::pow(2, current_attempt as u32) - 1); - cross_sleep(sleep_time).await; + cross_sleep(Duration::from_millis(sleep_time as u64)).await; current_attempt += 1; continue; // Retry! } NetmdStatus::Accepted | NetmdStatus::Implemented | NetmdStatus::Interim => { if current_attempt >= Self::MAX_INTERIM_READ_ATTEMPTS { - return Err("Max interim retry attempts reached".into()); + return Err(InterfaceError::MaxRetries); } return Ok(data); } - _ => return Err("Unknown return status".into()), + _ => return Err(InterfaceError::Unknown(format!("{:X?}", data))), } } // This should NEVER happen unless the code is changed wrongly - Err("The max retries is set to 0".into()) + unreachable!("The max number of retries is set to 0!") } - async fn playback_control(&mut self, action: Action) -> Result<(), Box> { + async fn playback_control(&mut self, action: Action) -> Result<(), InterfaceError> { let mut query = format_query( "18c3 ff %b 000000".to_string(), vec![QueryValue::Number(action as i64)], @@ -432,28 +488,28 @@ impl NetMDInterface { } /// Begin playback or resume after paused - pub async fn play(&mut self) -> Result<(), Box> { + pub async fn play(&mut self) -> Result<(), InterfaceError> { self.playback_control(Action::Play).await } /// Fast foward through the disc - pub async fn fast_forward(&mut self) -> Result<(), Box> { + pub async fn fast_forward(&mut self) -> Result<(), InterfaceError> { self.playback_control(Action::FastForward).await } /// Rewind through the disc - pub async fn rewind(&mut self) -> Result<(), Box> { + pub async fn rewind(&mut self) -> Result<(), InterfaceError> { self.playback_control(Action::Rewind).await } /// Pause playback - pub async fn pause(&mut self) -> Result<(), Box> { + pub async fn pause(&mut self) -> Result<(), InterfaceError> { self.playback_control(Action::Pause).await } //TODO: Implement fix for LAM-1 /// Stop playback - pub async fn stop(&mut self) -> Result<(), Box> { + pub async fn stop(&mut self) -> Result<(), InterfaceError> { let mut query = format_query("18c5 ff 00000000".to_string(), vec![])?; let reply = self.send_query(&mut query, false, false).await?; @@ -463,7 +519,7 @@ impl NetMDInterface { Ok(()) } - pub async fn acquire(&mut self) -> Result<(), Box> { + pub async fn acquire(&mut self) -> Result<(), InterfaceError> { let mut query = format_query("ff 010c ffff ffff ffff ffff ffff ffff".to_string(), vec![])?; let reply = self.send_query(&mut query, false, false).await?; @@ -472,7 +528,7 @@ impl NetMDInterface { Ok(()) } - pub async fn release(&mut self) -> Result<(), Box> { + pub async fn release(&mut self) -> Result<(), InterfaceError> { let mut query = format_query("ff 0100 ffff ffff ffff ffff ffff ffff".to_string(), vec![])?; let reply = self.send_query(&mut query, false, false).await?; @@ -482,12 +538,11 @@ impl NetMDInterface { Ok(()) } - pub async fn status(&mut self) -> Result, Box> { + pub async fn status(&mut self) -> Result, InterfaceError> { self.change_descriptor_state( &Descriptor::OperatingStatusBlock, &DescriptorAction::OpenRead, - ) - .await; + ).await?; let mut query = format_query( "1809 8001 0230 8800 0030 8804 00 ff00 00000000".to_string(), @@ -502,7 +557,7 @@ impl NetMDInterface { )?; self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close) - .await; + .await?; let final_array = res[0].to_vec().unwrap(); @@ -510,7 +565,7 @@ impl NetMDInterface { } /// Check if a disc is loaded in the player - pub async fn disc_present(&mut self) -> Result> { + pub async fn disc_present(&mut self) -> Result { let status = self.status().await?; println!("{:X?}", status); @@ -518,13 +573,14 @@ impl NetMDInterface { Ok(status[4] == 0x40) } - async fn full_operating_status(&mut self) -> Result<(u8, u16), Box> { + async fn full_operating_status(&mut self) -> Result<(u8, u16), InterfaceError> { // WARNING: Does not work for all devices. See https://github.com/cybercase/webminidisc/issues/21 self.change_descriptor_state( &Descriptor::OperatingStatusBlock, &DescriptorAction::OpenRead, ) - .await; + .await?; + let mut query = format_query( "1809 8001 0330 8802 0030 8805 0030 8806 00 ff00 00000000".to_string(), vec![], @@ -541,10 +597,10 @@ impl NetMDInterface { let status_mode = result[0].to_i64().unwrap() as u8; self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close) - .await; + .await?; if operating_status.len() < 2 { - return Err("Unparsable operating system".into()); + return Err(InterfaceError::InvalidStatus(StatusError)); } let operating_status_number = @@ -553,18 +609,19 @@ impl NetMDInterface { Ok((status_mode, operating_status_number)) } - pub async fn operating_status(&mut self) -> Result> { + pub async fn operating_status(&mut self) -> Result { let status = self.full_operating_status().await?.1; Ok(status) } - async fn playback_status_query(&mut self, p1: u32, p2: u32) -> Result, Box> { + async fn playback_status_query(&mut self, p1: u32, p2: u32) -> Result, InterfaceError> { self.change_descriptor_state( &Descriptor::OperatingStatusBlock, &DescriptorAction::OpenRead, ) - .await; + .await?; + let mut query = format_query( "1809 8001 0330 %w 0030 8805 0030 %w 00 ff00 00000000".to_string(), vec![QueryValue::Number(p1 as i64), QueryValue::Number(p2 as i64)], @@ -578,26 +635,26 @@ impl NetMDInterface { )?; self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close) - .await; + .await?; Ok(res[0].to_vec().unwrap()) } - pub async fn playback_status1(&mut self) -> Result, Box> { - Ok(self.playback_status_query(0x8801, 0x8807).await?) + pub async fn playback_status1(&mut self) -> Result, InterfaceError> { + self.playback_status_query(0x8801, 0x8807).await } - pub async fn playback_status2(&mut self) -> Result, Box> { - Ok(self.playback_status_query(0x8802, 0x8806).await?) + pub async fn playback_status2(&mut self) -> Result, InterfaceError> { + self.playback_status_query(0x8802, 0x8806).await } /// Get the current playback position - pub async fn position(&mut self) -> Result<[u16; 5], Box> { + pub async fn position(&mut self) -> Result<[u16; 5], InterfaceError> { self.change_descriptor_state( &Descriptor::OperatingStatusBlock, &DescriptorAction::OpenRead, ) - .await; + .await?; let mut query = format_query( "1809 8001 0430 8802 0030 8805 0030 0003 0030 0002 00 ff00 00000000".to_string(), @@ -607,8 +664,12 @@ impl NetMDInterface { let reply = match self.send_query(&mut query, false, false).await { Ok(result) => result, - Err(e) if e.to_string() == "Rejected" => Vec::new(), - Err(e) => return Err(e), + Err(e) => { + match e { + InterfaceError::Rejected(_) => Vec::new(), + _ => return Err(e) + } + }, }; let result = scan_query(reply, "1809 8001 0430 %?%? %?%? %?%? %?%? %?%? %?%? %?%? %? %?00 00%?0000 000b 0002 0007 00 %w %B %B %B %B".to_string())?; @@ -622,13 +683,13 @@ impl NetMDInterface { ]; self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close) - .await; + .await?; Ok(final_result) } /// Eject the disc from the player if supported - pub async fn eject_disc(&mut self) -> Result<(), Box> { + pub async fn eject_disc(&mut self) -> Result<(), InterfaceError> { let mut query = format_query("18c1 ff 6000".to_string(), vec![]).unwrap(); let _reply = self.send_query(&mut query, false, false).await?; @@ -636,7 +697,7 @@ impl NetMDInterface { } /// Check if the machine has the capability to eject a disc - pub async fn can_eject_disc(&mut self) -> Result> { + pub async fn can_eject_disc(&mut self) -> Result { let mut query = format_query("18c1 ff 6000".to_string(), vec![]).unwrap(); match self.send_query(&mut query, true, false).await { @@ -646,7 +707,7 @@ impl NetMDInterface { } /// Move the playback to a specific track - pub async fn go_to_track(&mut self, track_number: u16) -> Result> { + pub async fn go_to_track(&mut self, track_number: u16) -> Result { let mut query = format_query( "1850 ff010000 0000 %w".to_string(), vec![QueryValue::Number(track_number as i64)], @@ -657,7 +718,7 @@ impl NetMDInterface { let res = scan_query(reply, "1850 00010000 0000 %w".to_string())?; - let value = res[0].to_i64()?; + let value = res[0].to_i64().unwrap(); Ok(value as u16) } @@ -670,7 +731,7 @@ impl NetMDInterface { minute: u8, second: u8, frame: u8, - ) -> Result> { + ) -> Result { let mut query = format_query( "1850 ff000000 0000 %w %B%B%B%B".to_string(), vec![ @@ -687,12 +748,12 @@ impl NetMDInterface { let res = scan_query(reply, "1850 00000000 %?%? %w %B%B%B%B".to_string())?; - let value = res[0].to_i64()?; + let value = res[0].to_i64().unwrap(); Ok(value as u16) } - async fn track_change(&mut self, direction: Track) -> Result<(), Box> { + async fn track_change(&mut self, direction: Track) -> Result<(), InterfaceError> { let mut query = format_query( "1850 ff10 00000000 %w".to_string(), vec![QueryValue::Number(direction as i64)], @@ -707,22 +768,22 @@ impl NetMDInterface { } /// Change to the next track (skip forward) - pub async fn next_track(&mut self) -> Result<(), Box> { + pub async fn next_track(&mut self) -> Result<(), InterfaceError> { self.track_change(Track::Next).await } /// Change to the next track (skip back) - pub async fn previous_track(&mut self) -> Result<(), Box> { + pub async fn previous_track(&mut self) -> Result<(), InterfaceError> { self.track_change(Track::Previous).await } /// Change to the next track (skip to beginning of track) - pub async fn restart_track(&mut self) -> Result<(), Box> { + pub async fn restart_track(&mut self) -> Result<(), InterfaceError> { self.track_change(Track::Restart).await } /// Erase the disc entirely - pub async fn erase_disc(&mut self) -> Result<(), Box> { + pub async fn erase_disc(&mut self) -> Result<(), InterfaceError> { let mut query = format_query("1840 ff 0000".to_string(), vec![]).unwrap(); let reply = self.send_query(&mut query, false, false).await?; scan_query(reply, "1840 00 0000".to_string())?; @@ -731,9 +792,10 @@ impl NetMDInterface { // TODO: Ensure this is returning the correct value, it // looks like it actually might be a 16 bit integer - pub async fn disc_flags(&mut self) -> Result> { + pub async fn disc_flags(&mut self) -> Result { self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::OpenRead) - .await; + .await?; + let mut query = format_query("1806 01101000 ff00 0001000b".to_string(), vec![]).unwrap(); let reply = self.send_query(&mut query, false, false).await?; @@ -741,15 +803,15 @@ impl NetMDInterface { let res = scan_query(reply, "1806 01101000 1000 0001000b %b".to_string()).unwrap(); self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::Close) - .await; + .await?; Ok(res[0].to_i64().unwrap() as u8) } /// The number of tracks on the disc - pub async fn track_count(&mut self) -> Result> { + pub async fn track_count(&mut self) -> Result { self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead) - .await; + .await?; let mut query = format_query("1806 02101001 3000 1000 ff00 00000000".to_string(), vec![]).unwrap(); @@ -763,17 +825,17 @@ impl NetMDInterface { .unwrap(); self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close) - .await; + .await?; Ok(res[0].to_i64().unwrap() as u16) } /// Gets the disc title as it is stored - async fn raw_disc_title(&mut self, wchar: bool) -> Result> { + async fn raw_disc_title(&mut self, wchar: bool) -> Result { self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead) - .await; + .await?; self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::OpenRead) - .await; + .await?; let mut done: i32 = 0; let mut remaining: i32 = 0; @@ -828,15 +890,15 @@ impl NetMDInterface { let res = result.join(""); self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::Close) - .await; + .await?; self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close) - .await; + .await?; Ok(res) } /// Gets the disc title - pub async fn disc_title(&mut self, wchar: bool) -> Result> { + pub async fn disc_title(&mut self, wchar: bool) -> Result { let mut title = self.raw_disc_title(wchar).await?; let delim = match wchar { @@ -864,7 +926,7 @@ impl NetMDInterface { /// Gets all groups on the disc pub async fn track_group_list( &mut self, - ) -> Result, Option, Vec)>, Box> { + ) -> Result, Option, Vec)>, InterfaceError> { let raw_title = self.raw_disc_title(false).await?; let group_list = raw_title.split("//"); let mut track_dict: HashMap = HashMap::new(); @@ -886,7 +948,7 @@ impl NetMDInterface { let track_range: String = match group.split_once(';') { Some(string) => string.0.to_string(), - None => return Err("No groups were found".into()), + None => return Err(InterfaceError::GroupError("no groups were found".into())), }; if track_range.is_empty() { continue; @@ -924,7 +986,7 @@ impl NetMDInterface { for track in track_min - 1..track_max { if track_dict.contains_key(&track) { return Err( - format!("Track {track} is in 2 groups: {}", track_dict[&track].0).into(), + InterfaceError::GroupError(format!("track {} is in 2 groups", track)) ); } track_dict.insert(track, (String::from(group_name), i as u16)); @@ -952,7 +1014,7 @@ impl NetMDInterface { &mut self, tracks: Vec, wchar: bool, - ) -> Result, Box> { + ) -> Result, InterfaceError> { let wchar_value = match wchar { true => 3, false => 2, @@ -964,7 +1026,7 @@ impl NetMDInterface { }; self.change_descriptor_state(&descriptor_type, &DescriptorAction::OpenRead) - .await; + .await?; let mut track_titles: Vec = vec![]; for i in tracks { @@ -994,13 +1056,13 @@ impl NetMDInterface { } self.change_descriptor_state(&descriptor_type, &DescriptorAction::Close) - .await; + .await?; Ok(track_titles) } /// Gets the title of a single track at an index - pub async fn track_title(&mut self, track: u16, wchar: bool) -> Result> { + pub async fn track_title(&mut self, track: u16, wchar: bool) -> Result { let title = match self.track_titles([track].into(), wchar).await { Ok(titles) => titles[0].clone(), Err(error) if error.to_string() == "Rejected" => String::new(), @@ -1010,10 +1072,10 @@ impl NetMDInterface { } // Sets the title of the disc - pub async fn set_disc_title(&mut self, title: &str, wchar: bool) -> Result<(), Box> { + pub async fn set_disc_title(&mut self, title: &str, wchar: bool) -> Result<(), InterfaceError> { let current_title = self.raw_disc_title(wchar).await?; if current_title == title { - return Err("Title is already the same".into()); + return Err(InterfaceError::TitleError); } let new_title: Vec; @@ -1034,12 +1096,12 @@ impl NetMDInterface { if self.net_md_device.vendor_id() == 0x04dd { self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::OpenWrite) - .await + .await? } else { self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::Close) - .await; + .await?; self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::OpenWrite) - .await + .await? } let mut query = format_query( @@ -1056,14 +1118,14 @@ impl NetMDInterface { if self.net_md_device.vendor_id() == 0x04dd { self.change_descriptor_state(&Descriptor::AudioUTOC1TD, &DescriptorAction::Close) - .await + .await? } else { self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::Close) - .await; + .await?; self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::OpenRead) - .await; + .await?; self.change_descriptor_state(&Descriptor::DiscTitleTD, &DescriptorAction::Close) - .await; + .await?; } Ok(()) @@ -1075,7 +1137,7 @@ impl NetMDInterface { track: u16, title: &str, wchar: bool, - ) -> Result<(), Box> { + ) -> Result<(), InterfaceError> { let new_title: Vec; let (wchar_value, descriptor) = match wchar { true => { @@ -1102,7 +1164,8 @@ impl NetMDInterface { }; self.change_descriptor_state(&descriptor, &DescriptorAction::OpenWrite) - .await; + .await?; + let mut query = format_query( "1807 022018%b %w 3000 0a00 5000 %w 0000 %w %*".to_string(), vec![ @@ -1120,13 +1183,13 @@ impl NetMDInterface { "1807 022018%? %?%? 3000 0a00 5000 %?%? 0000 %?%?".to_string(), ); self.change_descriptor_state(&descriptor, &DescriptorAction::Close) - .await; + .await?; Ok(()) } /// Erases a track from the disc's UTOC - pub async fn erase_track(&mut self, track: u16) -> Result<(), Box> { + pub async fn erase_track(&mut self, track: u16) -> Result<(), InterfaceError> { let mut query = format_query( "1840 ff01 00 201001 %w".to_string(), vec![QueryValue::Number(track as i64)], @@ -1138,7 +1201,7 @@ impl NetMDInterface { } /// Moves a track to another index on the disc - pub async fn move_track(&mut self, source: u16, dest: u16) -> Result<(), Box> { + pub async fn move_track(&mut self, source: u16, dest: u16) -> Result<(), InterfaceError> { let mut query = format_query( "1843 ff00 00 201001 %w 201001 %w".to_string(), vec![ @@ -1158,9 +1221,9 @@ impl NetMDInterface { track: u16, p1: i32, p2: i32, - ) -> Result, Box> { + ) -> Result, InterfaceError> { self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead) - .await; + .await?; let mut query = format_query( "1806 02201001 %w %w %w ff00 00000000".to_string(), @@ -1178,7 +1241,7 @@ impl NetMDInterface { )?; self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close) - .await; + .await?; Ok(res[0].to_vec().unwrap()) } @@ -1187,11 +1250,12 @@ impl NetMDInterface { pub async fn track_lengths( &mut self, tracks: Vec, - ) -> Result, Box> { + ) -> Result, InterfaceError> { let mut times: Vec = vec![]; self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead) - .await; + .await?; + for track in tracks { let mut query = format_query( "1806 02201001 %w %w %w ff00 00000000".to_string(), @@ -1224,7 +1288,7 @@ impl NetMDInterface { } self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close) - .await; + .await?; Ok(times) } @@ -1233,12 +1297,12 @@ impl NetMDInterface { pub async fn track_length( &mut self, track: u16, - ) -> Result> { + ) -> Result { Ok(self.track_lengths([track].into()).await?[0]) } /// Gets the encoding of a track (SP, LP2, LP4) - pub async fn track_encoding(&mut self, track_number: u16) -> Result> { + pub async fn track_encoding(&mut self, track_number: u16) -> Result { let raw_value = self.raw_track_info(track_number, 0x3080, 0x0700).await?; let result = scan_query(raw_value, "07 0004 0110 %b %b".to_string())?; @@ -1246,17 +1310,18 @@ impl NetMDInterface { Ok(0x90) => Encoding::SP, Ok(0x92) => Encoding::LP2, Ok(0x93) => Encoding::LP4, - Ok(e) => return Err(format!("Encoding value {e} out of range (0x90..0x92)").into()), - Err(error) => return Err(error), + Ok(e) => return Err(InterfaceError::InvalidEncoding(e as u8)), + Err(_) => unreachable!(), }; Ok(final_encoding) } /// Gets a track's flags - pub async fn track_flags(&mut self, track: u16) -> Result> { + pub async fn track_flags(&mut self, track: u16) -> Result { self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::OpenRead) - .await; + .await?; + let mut query = format_query( "1806 01201001 %w ff00 00010008".to_string(), vec![QueryValue::Number(track as i64)], @@ -1266,15 +1331,16 @@ impl NetMDInterface { let res = scan_query(reply, "1806 01201001 %?%? 10 00 00010008 %b".to_string())?; self.change_descriptor_state(&Descriptor::AudioContentsTD, &DescriptorAction::Close) - .await; + .await?; Ok(res[0].to_i64().unwrap() as u8) } /// Gets the disc capacity as a [std::time::Duration] - pub async fn disc_capacity(&mut self) -> Result<[std::time::Duration; 3], Box> { + pub async fn disc_capacity(&mut self) -> Result<[std::time::Duration; 3], InterfaceError> { self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::OpenRead) - .await; + .await?; + let mut query = format_query("1806 02101000 3080 0300 ff00 00000000".to_string(), vec![])?; let reply = self.send_query(&mut query, false, false).await?; let mut result: [std::time::Duration; 3] = [std::time::Duration::from_secs(0); 3]; @@ -1298,17 +1364,18 @@ impl NetMDInterface { } self.change_descriptor_state(&Descriptor::RootTD, &DescriptorAction::Close) - .await; + .await?; Ok(result) } - pub async fn recording_parameters(&mut self) -> Result, Box> { + pub async fn recording_parameters(&mut self) -> Result, InterfaceError> { self.change_descriptor_state( &Descriptor::OperatingStatusBlock, &DescriptorAction::OpenRead, ) - .await; + .await?; + let mut query = format_query( "1809 8001 0330 8801 0030 8805 0030 8807 00 ff00 00000000".to_string(), vec![], @@ -1319,7 +1386,7 @@ impl NetMDInterface { let res = scan_query(reply, "1809 8001 0330 8801 0030 8805 0030 8807 00 1000 000e0000 000c 8805 0008 80e0 0110 %b %b 4000".to_string())?; self.change_descriptor_state(&Descriptor::OperatingStatusBlock, &DescriptorAction::Close) - .await; + .await?; Ok(res.into_iter().map(|x| x.to_i64().unwrap() as u8).collect()) } @@ -1330,7 +1397,7 @@ impl NetMDInterface { pub async fn save_track_to_array( &mut self, track: u16, - ) -> Result<(DiscFormat, u16, Vec), Box> { + ) -> Result<(DiscFormat, u16, Vec), InterfaceError> { let mut query = format_query( "1800 080046 f003010330 ff00 1001 %w".to_string(), vec![QueryValue::Number((track + 1) as i64)], @@ -1361,13 +1428,13 @@ impl NetMDInterface { 2 => DiscFormat::LP2, 4 => DiscFormat::SPMono, 6 => DiscFormat::SPStereo, - _ => return Err("DiscFormat out of range 0..6".into()), + e => return Err(InterfaceError::InvalidDiscFormat(e)), }; Ok((format, frames, result)) } - pub async fn disable_new_track_protection(&mut self, val: u16) -> Result<(), Box> { + pub async fn disable_new_track_protection(&mut self, val: u16) -> Result<(), InterfaceError> { let mut query = format_query( "1800 080046 f0030103 2b ff %w".to_string(), vec![QueryValue::Number(val as i64)], @@ -1378,7 +1445,7 @@ impl NetMDInterface { Ok(()) } - pub async fn enter_secure_session(&mut self) -> Result<(), Box> { + pub async fn enter_secure_session(&mut self) -> Result<(), InterfaceError> { let mut query = format_query("1800 080046 f0030103 80 ff".to_string(), vec![])?; let reply = self.send_query(&mut query, false, false).await?; @@ -1386,7 +1453,7 @@ impl NetMDInterface { Ok(()) } - pub async fn leave_secure_session(&mut self) -> Result<(), Box> { + pub async fn leave_secure_session(&mut self) -> Result<(), InterfaceError> { let mut query = format_query("1800 080046 f0030103 81 ff".to_string(), vec![])?; let reply = self.send_query(&mut query, false, false).await?; @@ -1400,7 +1467,7 @@ impl NetMDInterface { /// the root key. /// /// The leaf ID is a 8-byte constant - pub async fn leaf_id(&mut self) -> Result<[u8; 8], Box> { + pub async fn leaf_id(&mut self) -> Result<[u8; 8], InterfaceError> { let mut query = format_query("1800 080046 f0030103 11 ff".to_string(), vec![])?; let reply = self.send_query(&mut query, false, false).await?; @@ -1415,15 +1482,12 @@ impl NetMDInterface { keychain: [[u8; 16]; 2], depth: i32, ekbsignature: [u8; 24], - ) -> Result<(), Box> { + ) -> Result<(), InterfaceError> { let chainlen = keychain.len(); let databytes = 16 + 16 * chainlen + 24; if !(1..=63).contains(&depth) { - return Err("Supplied depth is invalid".into()); - } - if ekbsignature.len() != 24 { - return Err("Supplied EKB signature length wrong".into()); + return Err(EncryptionError::InvalidDepth(depth))?; } let keychains = keychain.concat(); @@ -1454,10 +1518,11 @@ impl NetMDInterface { pub async fn session_key_exchange( &mut self, hostnonce: Vec, - ) -> Result, Box> { + ) -> Result, InterfaceError> { if hostnonce.len() != 8 { - return Err("Supplied host nonce length wrong".into()); + return Err(EncryptionError::InvalidLength("host nonce", hostnonce.len()))?; } + let mut query = format_query( "1800 080046 f0030103 20 ff 000000 %*".to_string(), vec![QueryValue::Array(hostnonce)], @@ -1470,7 +1535,7 @@ impl NetMDInterface { Ok(res[0].to_vec().unwrap()) } - pub async fn session_key_forget(&mut self) -> Result<(), Box> { + pub async fn session_key_forget(&mut self) -> Result<(), InterfaceError> { let mut query = format_query("1800 080046 f0030103 21 ff 000000".to_string(), vec![])?; let reply = self.send_query(&mut query, false, false).await?; @@ -1484,15 +1549,15 @@ impl NetMDInterface { contentid: &[u8], keyenckey: &[u8], hex_session_key: &[u8], - ) -> Result<(), Box> { + ) -> Result<(), InterfaceError> { if contentid.len() != 20 { - return Err("Supplied content ID length wrong".into()); + return Err(EncryptionError::InvalidLength("content ID", contentid.len()))?; } if keyenckey.len() != 8 { - return Err("Supplied Key Encryption Key length wrong".into()); + return Err(EncryptionError::InvalidLength("key encryption", keyenckey.len()))?; } if hex_session_key.len() != 8 { - return Err("Supplied Session Key length wrong".into()); + return Err(EncryptionError::InvalidLength("session key", hex_session_key.len()))?; } let mut message = [vec![1, 1, 1, 1], contentid.to_vec(), keyenckey.to_vec()].concat(); @@ -1516,9 +1581,9 @@ impl NetMDInterface { &mut self, track_number: u16, hex_session_key: &[u8], - ) -> Result<(), Box> { + ) -> Result<(), InterfaceError> { if hex_session_key.len() != 8 { - return Err("Supplied Session Key length wrong".into()); + return Err(EncryptionError::InvalidLength("hex session key", hex_session_key.len()))?; } let mut message = [0u8; 8]; @@ -1552,16 +1617,16 @@ impl NetMDInterface { mut packets: UnboundedReceiver<(Vec, Vec, Vec)>, hex_session_key: &[u8], progress_callback: F, - ) -> Result<(u16, Vec, Vec), Box> + ) -> Result<(u16, Vec, Vec), InterfaceError> where F: Fn(usize, usize), { if hex_session_key.len() != 8 { - return Err("Supplied Session Key length wrong".into()); + return Err(EncryptionError::InvalidLength("hex session key", hex_session_key.len()))?; } // Sharps are slow - cross_sleep(200).await; + cross_sleep(Duration::from_millis(200)).await; let total_bytes: usize = (pkt_size + 24) as usize; //framesizedict[wireformat] * frames + pktcount * 24; @@ -1582,7 +1647,7 @@ impl NetMDInterface { self.net_md_device.poll().await?; // Sharps are slow - cross_sleep(200).await; + cross_sleep(Duration::from_millis(200)).await; let mut _written_bytes = 0; let mut packet_count = 0; @@ -1622,7 +1687,7 @@ impl NetMDInterface { Ok((res[0].to_i64().unwrap() as u16, part1, part2)) } - pub async fn track_uuid(&mut self, track: u16) -> Result> { + pub async fn track_uuid(&mut self, track: u16) -> Result { let mut query = format_query( "1800 080046 f0030103 23 ff 1001 %w".to_string(), vec![QueryValue::Number(track as i64)], @@ -1634,13 +1699,14 @@ impl NetMDInterface { Ok(String::from_utf8_lossy(&res[0].to_vec().unwrap()).to_string()) } - pub async fn terminate(&mut self) -> Result<(), Box> { + pub async fn terminate(&mut self) -> Result<(), InterfaceError> { let mut query = format_query("1800 080046 f0030103 2a ff00".to_string(), vec![])?; self.send_query(&mut query, false, false).await?; Ok(()) } } + type DesEcbEnc = ecb::Encryptor; type DesCbcEnc = cbc::Encryptor; type DesCbcDec = cbc::Decryptor; diff --git a/src/netmd/query_utils.rs b/src/netmd/query_utils.rs index f008a43..e8bd10d 100644 --- a/src/netmd/query_utils.rs +++ b/src/netmd/query_utils.rs @@ -1,7 +1,7 @@ use crate::netmd::utils; use lazy_static::lazy_static; use std::collections::hash_map::HashMap; -use std::error::Error; +use thiserror::Error; lazy_static! { /// %b, w, d, q - explained above (can have endiannes overriden by '>' and '<' operators, f. ex. %>d %), } +#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub enum ValueError { + #[error("type mismatch: expected {expected}, got {actual}")] + TypeMismatch { + expected: String, + actual: String + } +} + impl QueryValue { - pub fn to_vec(&self) -> Result, Box> { + pub fn from_array(value: [u8; S]) -> Self { + Self::Array(value.to_vec()) + } + + pub fn to_array(&self) -> Result<[u8; S], ValueError> { + let mut array = [0u8; S]; match self { - QueryValue::Array(a) => Ok(a.to_vec()), - _ => Err("QueryValue type mismatch! Expected Vec, got i64".into()), + QueryValue::Array(a) => { + for (i, byte) in a.iter().take(S).enumerate() { + array[i] = *byte + } + Ok(array) + }, + _ => Err(ValueError::TypeMismatch { + expected: String::from("Vec"), + actual: format!("{:?}", self) + }), } } - pub fn to_i64(&self) -> Result> { + pub fn to_vec(&self) -> Result, ValueError> { + match self { + QueryValue::Array(a) => Ok(a.to_vec()), + _ => Err(ValueError::TypeMismatch { + expected: String::from("Vec"), + actual: format!("{:?}", self) + }), + } + } + + pub fn to_i64(&self) -> Result { match self { QueryValue::Number(a) => Ok(*a), - _ => Err("QueryValue type mismatch! Expected i64, got Vec".into()), + _ => Err(ValueError::TypeMismatch { + expected: String::from("i64"), + actual: format!("{:?}", self) + }), } } } +#[derive(Error, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub enum QueryError { + #[error("unrecognized format character: `{0}`")] + UnrecognizedChar(char), + + #[error("Format and input mismatch at {index}: expected {expected:#04x}, got {actual:#04x} (format {format_string})")] + InputMismatch { + index: usize, + expected: u8, + actual: u8, + format_string: String, + } +} + /// Formats a query using a standard input to send to the player -pub fn format_query(format: String, args: Vec) -> Result, Box> { +pub fn format_query(format: String, args: Vec) -> Result, QueryError> { if DEBUG { println!("SENT>>> F: {}", format); } @@ -114,7 +163,7 @@ pub fn format_query(format: String, args: Vec) -> Result, Bo } result.push((converted & 0xFF) as u8); } - _ => return Err(format!("Unrecognized format char {}", character).into()), + _ => return Err(QueryError::UnrecognizedChar(character)), } continue; } @@ -142,7 +191,7 @@ pub fn format_query(format: String, args: Vec) -> Result, Bo pub fn scan_query( query_result: Vec, format: String, -) -> Result, Box> { +) -> Result, QueryError> { let mut result: Vec = Vec::new(); let initial_length = query_result.len(); @@ -224,7 +273,7 @@ pub fn scan_query( | input_stack.next().unwrap() as i32; result.push(QueryValue::Number(utils::bcd_to_int(v) as i64)); } - _ => return Err(format!("Unrecognized format char {}", character).into()), + _ => return Err(QueryError::UnrecognizedChar(character)), } continue; } @@ -244,7 +293,12 @@ pub fn scan_query( u8::from_str_radix(&String::from_iter([half.unwrap(), character]), 16).unwrap(); if format_value != input_value { let i = initial_length - input_stack.len() - 1; - return Err(format!("Format and input mismatch at {i}: expected {format_value:#04x}, got {input_value:#04x} (format {format})").into()); + return Err(QueryError::InputMismatch { + index: i, + expected: format_value, + actual: input_value, + format_string: format + }); } half = None; } diff --git a/src/netmd/utils.rs b/src/netmd/utils.rs index 90622f8..1c63dff 100644 --- a/src/netmd/utils.rs +++ b/src/netmd/utils.rs @@ -2,19 +2,19 @@ use crate::netmd::mappings::{ALLOWED_HW_KANA, MAPPINGS_DE, MAPPINGS_HW, MAPPINGS use diacritics; use encoding_rs::SHIFT_JIS; use regex::Regex; -use std::{collections::hash_map::HashMap, error::Error, vec::IntoIter}; +use std::{collections::hash_map::HashMap, error::Error, vec::IntoIter, time::Duration}; use unicode_normalization::UnicodeNormalization; extern crate kana; use kana::*; /// Sleep for a specified number of milliseconds on any platform -pub async fn cross_sleep(millis: u32) { +pub async fn cross_sleep(duration: Duration) { #[cfg(not(target_family = "wasm"))] - std::thread::sleep(std::time::Duration::from_millis(millis as u64)); + std::thread::sleep(duration); #[cfg(target_family = "wasm")] - gloo::timers::future::TimeoutFuture::new(millis).await; + gloo::timers::future::TimeoutFuture::new(duration.as_millis()).await; } pub fn bcd_to_int(mut bcd: i32) -> i32 {