Skip to content

Commit d755c45

Browse files
committed
Fix error handling
- Closes #4
1 parent c5150ae commit d755c45

File tree

3 files changed

+70
-60
lines changed

3 files changed

+70
-60
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ telemetry = ["winapi"]
1414
bitflags = "1.2"
1515
chrono = "0.4"
1616
encoding_rs = "0.8"
17+
libc = { version = "0.2", optional = true }
1718
serde = {version = "1.0", features = ["derive"] }
1819
serde_yaml = "0.8"
19-
winapi = {version = "0.3.9", features = ["std","memoryapi","winnt","errhandlingapi","synchapi","handleapi"], optional = true }
20+
thiserror = "1.0"
21+
winapi = {version = "0.3", features = ["std","memoryapi","winnt","errhandlingapi","synchapi","handleapi"], optional = true }
2022

2123
[package.metadata.docs.rs]
2224
default-target = "x86_64-pc-windows-msvc"

examples/dump_sample.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use serde_yaml::to_string;
33

44
pub fn main() {
55
let conn = Connection::new().expect("Unable to open telemetry");
6-
let telem = conn.telemetry().expect("Telem");
6+
let telem = conn.telemetry();
77

88
print!("{}", to_string(&telem.all()).unwrap());
99
}

src/telemetry.rs

Lines changed: 66 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@ use crate::session::*;
22
use encoding_rs::mem::decode_latin1;
33
use serde::{Deserialize, Serialize};
44
use serde_yaml::from_str as yaml_from;
5+
use std::borrow::Cow;
56
use std::convert::TryInto;
67
use std::default::Default;
7-
use std::error::Error;
88
use std::ffi::CStr;
9-
use std::fmt::{self, Display};
9+
use std::fmt;
1010
use std::io::Result as IOResult;
1111
use std::marker::PhantomData;
1212
use std::os::raw::{c_char, c_void};
1313
use std::os::windows::raw::HANDLE;
1414
use std::slice::from_raw_parts;
1515
use std::time::Duration;
16+
use thiserror::Error;
1617
use winapi::shared::minwindef::LPVOID;
1718
use winapi::um::errhandlingapi::GetLastError;
1819
use winapi::um::handleapi::CloseHandle;
@@ -196,47 +197,47 @@ impl Value {
196197
}
197198

198199
impl TryInto<i32> for Value {
199-
type Error = &'static str;
200+
type Error = ValueError;
200201

201202
fn try_into(self) -> Result<i32, Self::Error> {
202203
match self {
203204
Self::INT(n) => Ok(n),
204-
_ => Err("Value is not a signed 4-byte integer"),
205+
_ => Err(ValueError::TryInto("Value is not a signed 4-byte integer")),
205206
}
206207
}
207208
}
208209

209210
impl TryInto<u32> for Value {
210-
type Error = &'static str;
211+
type Error = ValueError;
211212

212213
fn try_into(self) -> Result<u32, Self::Error> {
213214
match self {
214215
Self::INT(n) => Ok(n as u32),
215216
Self::BITS(n) => Ok(n),
216-
_ => Err("Value is not a 4-byte integer"),
217+
_ => Err(ValueError::TryInto("Value is not a 4-byte integer")),
217218
}
218219
}
219220
}
220221

221222
impl TryInto<f32> for Value {
222-
type Error = &'static str;
223+
type Error = ValueError;
223224

224225
fn try_into(self) -> Result<f32, Self::Error> {
225226
match self {
226227
Self::FLOAT(n) => Ok(n),
227-
_ => Err("Value is not a float"),
228+
_ => Err(ValueError::TryInto("Value is not a float")),
228229
}
229230
}
230231
}
231232

232233
impl TryInto<f64> for Value {
233-
type Error = &'static str;
234+
type Error = ValueError;
234235

235236
fn try_into(self) -> Result<f64, Self::Error> {
236237
match self {
237238
Self::DOUBLE(n) => Ok(n),
238239
Self::FLOAT(f) => Ok(f as f64),
239-
_ => Err("Value is not a float or double"),
240+
_ => Err(ValueError::TryInto("Value is not a float or double")),
240241
}
241242
}
242243
}
@@ -303,7 +304,7 @@ impl Default for ValueHeader {
303304
}
304305

305306
impl fmt::Debug for ValueHeader {
306-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
307+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307308
write!(
308309
f,
309310
"ValueHeader(name=\"{}\", type={}, count={}, offset={})",
@@ -351,16 +352,12 @@ impl Header {
351352
content
352353
}
353354

354-
fn telemetry(&self, from_loc: *const c_void) -> Result<Sample, Box<dyn std::error::Error>> {
355+
fn telemetry(&self, from_loc: *const c_void) -> Sample {
355356
let (tick, vbh) = self.latest_buffer();
356357
let value_buffer = self.var_buffer(vbh, from_loc);
357358
let value_header = self.get_var_header(from_loc);
358359

359-
Ok(Sample::new(
360-
tick,
361-
value_header.to_vec(),
362-
value_buffer.to_vec(),
363-
))
360+
Sample::new(tick, value_header.to_vec(), value_buffer.to_vec())
364361
}
365362
}
366363

@@ -425,9 +422,9 @@ impl Sample {
425422
///
426423
/// `name` Name of the telemetry variable to get
427424
/// - see the iRacing Telemtry documentation for a complete list of possible values
428-
pub fn get(&self, name: &'static str) -> Result<Value, String> {
425+
pub fn get(&self, name: &'static str) -> Result<Value, SampleError> {
429426
match self.header_for(name) {
430-
None => Err(format!("No value '{}' found", name)),
427+
None => Err(SampleError::NoValue(format!("No value '{}' found", name))),
431428
Some(vh) => Ok(self.value(&vh)),
432429
}
433430
}
@@ -499,31 +496,51 @@ impl Sample {
499496
}
500497
}
501498

502-
///
503-
/// Telemetry Error
504-
///
505499
/// An error which occurs when telemetry samples cannot be read from the memory buffer.
506-
#[derive(Debug)]
500+
#[derive(Debug, Error)]
507501
pub enum TelemetryError {
508-
ABANDONED,
509-
TIMEOUT(usize),
510-
UNKNOWN(u32),
502+
#[error("Invalid duration: {0}")]
503+
InvalidDuration(#[from] std::num::TryFromIntError),
504+
505+
#[error("Windows Mutex was abandoned.")]
506+
Abandoned,
507+
508+
#[error("Timeout elapsed while waiting for a signal. Duration={0}")]
509+
Timeout(usize),
510+
511+
#[error("Unknown error occurred: {0}")]
512+
Unknown(u32),
513+
514+
#[error("Wait failed: {0}")]
515+
WaitFailed(std::io::Error),
511516
}
512517

513-
impl Display for TelemetryError {
514-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
515-
match self {
516-
Self::ABANDONED => write!(f, "Abandoned"),
517-
Self::TIMEOUT(ms) => write!(f, "Timeout after {}ms", ms),
518-
Self::UNKNOWN(v) => write!(f, "Unknown error code = {:x?}", v),
519-
}
520-
}
518+
/// An error which occurs while retrieving session info.
519+
#[derive(Debug, Error)]
520+
pub enum SessionError {
521+
#[error("Text decode error: {0}")]
522+
Decode(Cow<'static, str>),
523+
524+
#[error("YAML parse error: {0}")]
525+
ParseYAML(#[from] serde_yaml::Error),
521526
}
522527

523-
impl Error for TelemetryError {}
528+
/// An error which occurs when value conversions fail.
529+
#[derive(Debug, Error)]
530+
pub enum ValueError {
531+
#[error("{0}")]
532+
TryInto(&'static str),
533+
}
534+
535+
/// An error which may occur when reading values from a sample.
536+
#[derive(Debug, Error)]
537+
pub enum SampleError {
538+
#[error("{0}")]
539+
NoValue(String),
540+
}
524541

525542
impl<'conn> Blocking<'conn> {
526-
fn new(location: *const c_void) -> std::io::Result<Self> {
543+
fn new(location: *const c_void) -> IOResult<Self> {
527544
let mut event_name: Vec<u16> = DATA_EVENT_NAME.encode_utf16().collect();
528545
event_name.push(0);
529546

@@ -575,29 +592,26 @@ impl<'conn> Blocking<'conn> {
575592
/// # Ok(())
576593
/// # }
577594
/// ```
578-
pub fn sample(&self, timeout: Duration) -> Result<Sample, Box<dyn Error>> {
579-
let wait_time: u32 = match timeout.as_millis().try_into() {
580-
Ok(v) => v,
581-
Err(e) => return Err(Box::new(e)),
582-
};
595+
pub fn sample(&self, timeout: Duration) -> Result<Sample, TelemetryError> {
596+
let wait_time: u32 = timeout.as_millis().try_into()?;
583597

584598
unsafe {
585599
let signal = WaitForSingleObject(self.event_handle, wait_time);
586600

587601
match signal {
588-
0x80 => Err(Box::new(TelemetryError::ABANDONED)), // Abandoned
589-
0x102 => Err(Box::new(TelemetryError::TIMEOUT(wait_time as usize))), // Timeout
602+
0x80 => Err(TelemetryError::Abandoned),
603+
0x102 => Err(TelemetryError::Timeout(wait_time as usize)),
590604
0xFFFFFFFF => {
591605
// Error
592-
let errno = GetLastError() as i32;
593-
Err(Box::new(std::io::Error::from_raw_os_error(errno)))
606+
let errno = std::io::Error::from_raw_os_error(GetLastError() as i32);
607+
Err(TelemetryError::WaitFailed(errno))
594608
}
595609
0x00 => {
596610
// OK
597611
ResetEvent(self.event_handle);
598-
self.read_header().telemetry(self.origin)
612+
Ok(self.read_header().telemetry(self.origin))
599613
}
600-
_ => Err(Box::new(TelemetryError::UNKNOWN(signal as u32))),
614+
_ => Err(TelemetryError::Unknown(signal as u32)),
601615
}
602616
}
603617
}
@@ -688,7 +702,7 @@ impl Connection {
688702
/// Err(e) => println!("Invalid Session")
689703
/// };
690704
/// ```
691-
pub fn session_info(&self) -> Result<SessionDetails, Box<dyn std::error::Error>> {
705+
pub fn session_info(&self) -> Result<SessionDetails, SessionError> {
692706
let header = self.read_header();
693707

694708
let start = (self.location as usize + header.session_info_offset as usize) as *const u8;
@@ -711,14 +725,14 @@ impl Connection {
711725
/// # Examples
712726
///
713727
/// ```
714-
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
728+
/// # fn main() -> Result<(), std::io::Error> {
715729
/// use iracing::telemetry::Connection;
716730
///
717-
/// let sample = Connection::new()?.telemetry()?;
731+
/// let sample = Connection::new()?.telemetry();
718732
/// # Ok(())
719733
/// # }
720734
/// ```
721-
pub fn telemetry(&self) -> Result<Sample, Box<dyn std::error::Error>> {
735+
pub fn telemetry(&self) -> Sample {
722736
let header = self.read_header();
723737
header.telemetry(self.location as *const std::ffi::c_void)
724738
}
@@ -788,12 +802,6 @@ mod tests {
788802
fn test_latest_telemetry() {
789803
let session_tick: u32 = Connection::new()
790804
.expect("Unable to open telemetry")
791-
.telemetry()
792-
.expect("Couldn't get latest telem")
793-
.get("SessionTick")
794-
.unwrap()
795-
.try_into()
796-
.unwrap();
797-
assert!(session_tick > 0);
805+
.telemetry();
798806
}
799807
}

0 commit comments

Comments
 (0)