Skip to content

Commit

Permalink
Allow customising error types
Browse files Browse the repository at this point in the history
  • Loading branch information
illicitonion committed Aug 13, 2023
1 parent 55f0b35 commit 2899217
Show file tree
Hide file tree
Showing 10 changed files with 524 additions and 54 deletions.
9 changes: 8 additions & 1 deletion num_enum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ pub trait FromPrimitive: Sized {

pub trait TryFromPrimitive: Sized {
type Primitive: Copy + Eq + fmt::Debug;
type Error;

const NAME: &'static str;

fn try_from_primitive(number: Self::Primitive) -> Result<Self, TryFromPrimitiveError<Self>>;
fn try_from_primitive(number: Self::Primitive) -> Result<Self, Self::Error>;
}

pub trait UnsafeFromPrimitive: Sized {
Expand Down Expand Up @@ -56,6 +57,12 @@ pub struct TryFromPrimitiveError<Enum: TryFromPrimitive> {
pub number: Enum::Primitive,
}

impl<Enum: TryFromPrimitive> TryFromPrimitiveError<Enum> {
pub fn new(number: Enum::Primitive) -> Self {
Self { number }
}
}

impl<Enum: TryFromPrimitive> fmt::Debug for TryFromPrimitiveError<Enum> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("TryFromPrimitiveError")
Expand Down
6 changes: 6 additions & 0 deletions num_enum/tests/renamed_num_enum.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::process::Stdio;

#[test]
fn no_std() {
assert!(::std::process::Command::new("cargo")
Expand All @@ -9,6 +11,8 @@ fn no_std() {
"/../renamed_num_enum/Cargo.toml",
),
])
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.unwrap()
.success())
Expand All @@ -27,6 +31,8 @@ fn std() {
"--features",
"std",
])
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.unwrap()
.success())
Expand Down
56 changes: 56 additions & 0 deletions num_enum/tests/try_build/compile_fail/custom_error_type_parsing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#[derive(num_enum::TryFromPrimitive)]
#[num_enum(error_type(name = CustomError))]
#[repr(u8)]
enum MissingConstructor {
Zero,
One,
Two,
}

#[derive(num_enum::TryFromPrimitive)]
#[num_enum(error_type(constructor = CustomError::new))]
#[repr(u8)]
enum MissingName {
Zero,
One,
Two,
}

#[derive(num_enum::TryFromPrimitive)]
#[num_enum(error_type(name = CustomError, constructor = CustomError::new, extra = something))]
#[repr(u8)]
enum ExtraAttr {
Zero,
One,
Two,
}

#[derive(num_enum::TryFromPrimitive)]
#[num_enum(error_type(name = CustomError, constructor = CustomError::new), error_type(name = CustomError, constructor = CustomError::new))]
#[repr(u8)]
enum TwoErrorTypes {
Zero,
One,
Two,
}

#[derive(num_enum::TryFromPrimitive)]
#[num_enum(error_type(name = CustomError, constructor = CustomError::new))]
#[num_enum(error_type(name = CustomError, constructor = CustomError::new))]
#[repr(u8)]
enum TwoAttrs {
Zero,
One,
Two,
}

struct CustomError {}

impl CustomError {
fn new(_: u8) -> CustomError {
CustomError{}
}
}

fn main() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
error: num_enum error_type attribute requires `constructor` value
--> tests/try_build/compile_fail/custom_error_type_parsing.rs:2:12
|
2 | #[num_enum(error_type(name = CustomError))]
| ^^^^^^^^^^

error: num_enum error_type attribute requires `name` value
--> tests/try_build/compile_fail/custom_error_type_parsing.rs:11:12
|
11 | #[num_enum(error_type(constructor = CustomError::new))]
| ^^^^^^^^^^

error: expected `name` or `constructor`
--> tests/try_build/compile_fail/custom_error_type_parsing.rs:20:75
|
20 | #[num_enum(error_type(name = CustomError, constructor = CustomError::new, extra = something))]
| ^^^^^

error: num_enum attribute must have at most one error_type
--> tests/try_build/compile_fail/custom_error_type_parsing.rs:29:76
|
29 | #[num_enum(error_type(name = CustomError, constructor = CustomError::new), error_type(name = CustomError, constructor = CustomError::new))]
| ^^^^^^^^^^

error: At most one num_enum error_type attribute may be specified
--> tests/try_build/compile_fail/custom_error_type_parsing.rs:39:1
|
39 | #[num_enum(error_type(name = CustomError, constructor = CustomError::new))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
41 changes: 41 additions & 0 deletions num_enum/tests/try_from_primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,47 @@ fn try_from_primitive_number() {
assert_eq!(try_from, Ok(Enum::Whatever));
}

#[test]
fn custom_error() {
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[num_enum(error_type(name = CustomError, constructor = CustomError::new))]
#[repr(u8)]
enum FirstNumber {
Zero,
One,
Two,
}

#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[num_enum(error_type(constructor = CustomError::new, name = CustomError))]
#[repr(u8)]
enum SecondNumber {
Zero,
One,
Two,
}

#[derive(Debug, PartialEq, Eq)]
struct CustomError {
bad_value: u8,
}

impl CustomError {
fn new(value: u8) -> CustomError {
CustomError { bad_value: value }
}
}

let zero: Result<FirstNumber, _> = 0u8.try_into();
assert_eq!(zero, Ok(FirstNumber::Zero));

let three: Result<FirstNumber, _> = 3u8.try_into();
assert_eq!(three.unwrap_err(), CustomError { bad_value: 3u8 });

let three: Result<SecondNumber, _> = 3u8.try_into();
assert_eq!(three.unwrap_err(), CustomError { bad_value: 3u8 });
}

// #[derive(FromPrimitive)] generates implementations for the following traits:
//
// - `FromPrimitive<T>`
Expand Down
3 changes: 3 additions & 0 deletions num_enum_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ proc-macro2 = "1.0.60"
proc-macro-crate = { version = "1", optional = true }
quote = "1"
syn = { version = "2", features = ["parsing"] }

[dev-dependencies]
syn = { version = "2", features = ["extra-traits", "parsing"] }
Loading

0 comments on commit 2899217

Please sign in to comment.