Skip to content

Commit

Permalink
Make driver error handling conversion-friendly
Browse files Browse the repository at this point in the history
  • Loading branch information
RReverser committed May 26, 2023
1 parent b9f3f1c commit 730d4be
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 22 deletions.
33 changes: 33 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ tracing-subscriber = "0.3.16"
tracing-error = "0.2.0"
color-eyre = "0.6.2"
tracing-forest = { version = "0.1.5", features = ["ansi"] }
displaydoc = "0.2.4"
enum-tag = "0.3.0"

[[bench]]
name = "image_array"
Expand Down
23 changes: 20 additions & 3 deletions examples/camera-server.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use ascom_alpaca::api::{Camera, CameraState, CargoServerInfo, Device, ImageArray, SensorType};
use ascom_alpaca::{ASCOMError, ASCOMResult, Server};
use ascom_alpaca::{ASCOMError, ASCOMErrorCode, ASCOMResult, Server};
use async_trait::async_trait;
use eyre::ContextCompat;
use ndarray::Array3;
Expand Down Expand Up @@ -113,8 +113,25 @@ struct Webcam {
}

fn convert_err(nokhwa: NokhwaError) -> ASCOMError {
// TODO: more granular errors
ASCOMError::driver_error::<0>(nokhwa)
ASCOMError::new(
ASCOMErrorCode::new_for_driver(match nokhwa {
NokhwaError::UnitializedError => 0,
NokhwaError::InitializeError { .. } => 1,
NokhwaError::ShutdownError { .. } => 2,
NokhwaError::GeneralError(_) => 3,
NokhwaError::StructureError { .. } => 4,
NokhwaError::OpenDeviceError(_, _) => 5,
NokhwaError::GetPropertyError { .. } => 6,
NokhwaError::SetPropertyError { .. } => 7,
NokhwaError::OpenStreamError(_) => 8,
NokhwaError::ReadFrameError(_) => 9,
NokhwaError::ProcessFrameError { .. } => 10,
NokhwaError::StreamShutdownError(_) => 11,
NokhwaError::UnsupportedOperationError(_) => 12,
NokhwaError::NotImplementedError(_) => 13,
}),
nokhwa,
)
}

impl Webcam {
Expand Down
48 changes: 29 additions & 19 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,41 @@ const DRIVER_BASE: u16 = 0x500;
const MAX: u16 = 0xFFF;

impl ASCOMErrorCode {
/// Generate a driver-specific error code (supply code starting from `0`).
/// Generate ASCOM error code from a zero-based driver error code.
///
/// This is intentionally limited to be usable only in `const` contexts
/// so that you don't accidentally supply invalid values.
/// You'll typically want to define an enum for your driver errors and use this in a single
/// place - in the [`From`] conversion from your driver error type to the [`ASCOMError`].
///
/// # Example
///
/// The following example defines an error enum using [`displaydoc`] for error messages and
/// [`enum_tag`] for error codes.
///
/// ```
/// # use ascom_alpaca::{ASCOMError, ASCOMErrorCode};
/// // Create constants for driver-specific error codes.
/// const MY_ERROR_CODE: ASCOMErrorCode = ASCOMErrorCode::new_for_driver::<0>();
/// const MY_OTHER_ERROR_CODE: ASCOMErrorCode = ASCOMErrorCode::new_for_driver::<1>();
/// // ...somewhere later...
/// let error = ASCOMError::new(MY_ERROR_CODE, "Something went wrong");
/// use ascom_alpaca::{ASCOMError, ASCOMErrorCode};
/// use displaydoc::Display;
/// use enum_tag::EnumTag;
/// use thiserror::Error;
///
/// #[derive(Debug, Display, EnumTag, Error)]
/// #[repr(u16)]
/// pub enum MyDriverError {
/// /// Port communication error: {0}
/// PortError(std::io::Error) = 0,
/// /// Initialization error: {0}
/// InitializationError(String) = 1,
/// }
///
/// // this allows you to then use `my_driver.method()?` when implementing Alpaca traits
/// // and it will convert your driver error to an ASCOM error automatically
/// impl From<MyDriverError> for ASCOMError {
/// fn from(error: MyDriverError) -> Self {
/// ASCOMError::new(ASCOMErrorCode::new_for_driver(error.tag() as u16), error)
/// }
/// }
/// ```
pub const fn new_for_driver<const CODE: u16>() -> Self {
let raw = match CODE.checked_add(DRIVER_BASE) {
pub fn new_for_driver(driver_code: u16) -> Self {
let raw = match driver_code.checked_add(DRIVER_BASE) {
Some(raw) if raw <= MAX => raw,
_ => panic!("Driver error code is too large"),
};
Expand Down Expand Up @@ -166,14 +184,6 @@ ascom_error_codes! {
}

impl ASCOMError {
/// Generate a driver-specific error with the given code (0-based) and a message.
pub fn driver_error<const CODE: u16>(message: impl std::fmt::Display) -> Self {
Self {
code: ASCOMErrorCode::new_for_driver::<CODE>(),
message: message.to_string().into(),
}
}

/// Create a new "invalid operation" error with the specified message.
pub fn invalid_operation(message: impl std::fmt::Display) -> Self {
Self::new(ASCOMErrorCode::INVALID_OPERATION, message)
Expand Down

0 comments on commit 730d4be

Please sign in to comment.