Skip to content

Commit

Permalink
Use Arb.Error exception for errors
Browse files Browse the repository at this point in the history
  • Loading branch information
adriankumpf committed Aug 19, 2022
1 parent 244ba26 commit d755c7c
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 46 deletions.
6 changes: 3 additions & 3 deletions lib/arb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ defmodule Arb do
:ok
"""
@spec activate([relay_id], Keyword.t()) :: :ok | {:error, term}
@spec activate([relay_id], Keyword.t()) :: :ok | {:error, Arb.Error.t()}
def activate(ids, opts \\ []) when is_list(ids) do
opts = NimbleOptions.validate!(opts, port: @port_definition, verify: @verify_definition)
__activate__(ids, opts[:verify], opts[:port]) |> to_ok()
Expand All @@ -57,7 +57,7 @@ defmodule Arb do
{:ok, [1, 3, 6]}
"""
@spec get_active(Keyword.t()) :: {:ok, [relay_id]} | {:error, term}
@spec get_active(Keyword.t()) :: {:ok, [relay_id]} | {:error, Arb.Error.t()}
def get_active(opts \\ []) do
opts = NimbleOptions.validate!(opts, port: @port_definition)
__get_active__(opts[:port])
Expand All @@ -83,7 +83,7 @@ defmodule Arb do
:ok
"""
@spec reset(Keyword.t()) :: {:ok, [relay_id]} | {:error, term}
@spec reset(Keyword.t()) :: {:ok, [relay_id]} | {:error, Arb.Error.t()}
def reset(opts \\ []) do
opts = NimbleOptions.validate!(opts, port: @port_definition)
__reset__(opts[:port]) |> to_ok()
Expand Down
32 changes: 32 additions & 0 deletions lib/arb/error.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
defmodule Arb.Error do
@moduledoc """
An exception for errors returned by the `arb` library.
"""
@moduledoc since: "0.9.0"

@type reason ::
:not_found
| :multiple_found
| :verification_failed
| :unsafe_read
| :bad_device
| {:io, String.t()}
| {:usb, String.t()}

@type t :: %__MODULE__{reason: reason}

defexception [:reason]

@impl true
def message(%__MODULE__{reason: reason}) do
case reason do
:not_found -> "no relay board found"
:multiple_found -> "multiple relay boards found"
:verification_failed -> "verification failed"
:unsafe_read -> "Reading would exceeded the expected buffer size"
:bad_device -> "Usb device malfunction"
{:io, message} -> "I/O operation failed: #{message}"
{:usb, message} -> "libusb error: #{message}"
end
end
end
50 changes: 25 additions & 25 deletions native/arb/Cargo.lock

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

46 changes: 28 additions & 18 deletions native/arb/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,53 @@
use rustler::{Atom, NifTuple, NifUntaggedEnum};
use rustler::{Atom, NifException, NifTuple, NifUntaggedEnum};

#[derive(NifTuple, Debug)]
struct ErrorMessage(Atom, String);
struct ErrorTuple(Atom, String);

#[derive(NifUntaggedEnum, Debug)]
enum Reason {
Atom(Atom),
Tuple(ErrorMessage),
Tuple(ErrorTuple),
}

impl Reason {
fn new(reason: Atom) -> Self {
Self::Atom(reason)
#[derive(NifException, Debug)]
#[module = "Arb.Error"]
struct ArbError {
reason: Reason,
}

impl ArbError {
pub fn new(reason: Atom) -> Self {
Self {
reason: Reason::Atom(reason),
}
}
fn from_error(reason: Atom, error: impl std::error::Error) -> Self {
Self::Tuple(ErrorMessage(reason, error.to_string()))
Self {
reason: Reason::Tuple(ErrorTuple(reason, error.to_string())),
}
}
}

impl From<arb::Error> for Reason {
impl From<arb::Error> for ArbError {
fn from(err: arb::Error) -> Self {
mod atom {
rustler::atoms! { not_found, multiple_found, verification_failed, unsafe_read, bad_device, usb, io }
}

match err {
arb::Error::NotFound => Reason::new(atom::not_found()),
arb::Error::MultipleFound => Reason::new(atom::multiple_found()),
arb::Error::VerificationFailed => Reason::new(atom::verification_failed()),
arb::Error::UnsafeRead => Reason::new(atom::unsafe_read()),
arb::Error::BadDevice => Reason::new(atom::bad_device()),
arb::Error::Usb(e) => Reason::from_error(atom::usb(), e),
arb::Error::IO(e) => Reason::from_error(atom::io(), e),
arb::Error::NotFound => ArbError::new(atom::not_found()),
arb::Error::MultipleFound => ArbError::new(atom::multiple_found()),
arb::Error::VerificationFailed => ArbError::new(atom::verification_failed()),
arb::Error::UnsafeRead => ArbError::new(atom::unsafe_read()),
arb::Error::BadDevice => ArbError::new(atom::bad_device()),
arb::Error::Usb(e) => ArbError::from_error(atom::usb(), e),
arb::Error::IO(e) => ArbError::from_error(atom::io(), e),
}
}
}

#[rustler::nif(schedule = "DirtyIo", name = "__activate__")]
fn activate(relays: Vec<u8>, verify: bool, port: Option<u8>) -> Result<(), Reason> {
fn activate(relays: Vec<u8>, verify: bool, port: Option<u8>) -> Result<(), ArbError> {
let relays = relays
.into_iter()
.filter(|r| *r != 0)
Expand All @@ -47,7 +57,7 @@ fn activate(relays: Vec<u8>, verify: bool, port: Option<u8>) -> Result<(), Reaso
}

#[rustler::nif(schedule = "DirtyIo", name = "__get_active__")]
fn get_active(port: Option<u8>) -> Result<Vec<u8>, Reason> {
fn get_active(port: Option<u8>) -> Result<Vec<u8>, ArbError> {
let result = arb::get_status(port)?;

let active_relays = (0..8)
Expand All @@ -61,7 +71,7 @@ fn get_active(port: Option<u8>) -> Result<Vec<u8>, Reason> {
}

#[rustler::nif(schedule = "DirtyIo", name = "__reset__")]
fn reset(port: Option<u8>) -> Result<(), Reason> {
fn reset(port: Option<u8>) -> Result<(), ArbError> {
Ok(arb::reset(port)?)
}

Expand Down

0 comments on commit d755c7c

Please sign in to comment.