From d755c7caf7ff12c519880bd68a0f3feeb213afcf Mon Sep 17 00:00:00 2001 From: Adrian Kumpf <8999358+adriankumpf@users.noreply.github.com> Date: Fri, 19 Aug 2022 18:47:50 +0200 Subject: [PATCH] Use `Arb.Error` exception for errors --- lib/arb.ex | 6 +++--- lib/arb/error.ex | 32 +++++++++++++++++++++++++++ native/arb/Cargo.lock | 50 +++++++++++++++++++++---------------------- native/arb/src/lib.rs | 46 +++++++++++++++++++++++---------------- 4 files changed, 88 insertions(+), 46 deletions(-) create mode 100644 lib/arb/error.ex diff --git a/lib/arb.ex b/lib/arb.ex index 52f7f28..6a188fe 100644 --- a/lib/arb.ex +++ b/lib/arb.ex @@ -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() @@ -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]) @@ -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() diff --git a/lib/arb/error.ex b/lib/arb/error.ex new file mode 100644 index 0000000..32243df --- /dev/null +++ b/lib/arb/error.ex @@ -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 diff --git a/native/arb/Cargo.lock b/native/arb/Cargo.lock index aa79a4b..76decf7 100644 --- a/native/arb/Cargo.lock +++ b/native/arb/Cargo.lock @@ -49,15 +49,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.125" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "libusb1-sys" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dfab089105aa85a3b492b421bd90d55e6257f00f8447cc3873c44f8206809ce" +checksum = "f9d0e2afce4245f2c9a418511e5af8718bcaf2fa408aefb259504d1a9cb25f27" dependencies = [ "cc", "libc", @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "pkg-config" @@ -79,27 +79,27 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -108,9 +108,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "rusb" @@ -157,29 +157,29 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.92" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" dependencies = [ "proc-macro2", "quote", @@ -187,10 +187,10 @@ dependencies = [ ] [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-ident" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" [[package]] name = "unreachable" diff --git a/native/arb/src/lib.rs b/native/arb/src/lib.rs index 71a424b..76ee767 100644 --- a/native/arb/src/lib.rs +++ b/native/arb/src/lib.rs @@ -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 for Reason { +impl From 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, verify: bool, port: Option) -> Result<(), Reason> { +fn activate(relays: Vec, verify: bool, port: Option) -> Result<(), ArbError> { let relays = relays .into_iter() .filter(|r| *r != 0) @@ -47,7 +57,7 @@ fn activate(relays: Vec, verify: bool, port: Option) -> Result<(), Reaso } #[rustler::nif(schedule = "DirtyIo", name = "__get_active__")] -fn get_active(port: Option) -> Result, Reason> { +fn get_active(port: Option) -> Result, ArbError> { let result = arb::get_status(port)?; let active_relays = (0..8) @@ -61,7 +71,7 @@ fn get_active(port: Option) -> Result, Reason> { } #[rustler::nif(schedule = "DirtyIo", name = "__reset__")] -fn reset(port: Option) -> Result<(), Reason> { +fn reset(port: Option) -> Result<(), ArbError> { Ok(arb::reset(port)?) }