From 78737e1583bbe07f596a27b65fe70c55e1c31af8 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 25 Jan 2023 20:20:48 +0200 Subject: [PATCH 1/6] Add `ops::UnitError` --- impl/src/add_like.rs | 6 +++-- impl/src/not_like.rs | 11 +++++---- src/errors/mod.rs | 4 ++++ src/errors/ops.rs | 32 +++++++++++++++++++++++++++ src/{errors.rs => errors/try_into.rs} | 0 src/lib.rs | 6 +++-- 6 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 src/errors/mod.rs create mode 100644 src/errors/ops.rs rename src/{errors.rs => errors/try_into.rs} (100%) diff --git a/impl/src/add_like.rs b/impl/src/add_like.rs index 5fda73d3..44015247 100644 --- a/impl/src/add_like.rs +++ b/impl/src/add_like.rs @@ -121,9 +121,11 @@ fn enum_content( matches.push(matcher); } Fields::Unit => { - let message = format!("Cannot {method_ident}() unit variants"); + let operation_name = method_ident.to_string(); matches.push(quote! { - (#subtype, #subtype) => ::core::result::Result::Err(#message) + (#subtype, #subtype) => ::core::result::Result::Err( + ::derive_more::OpsUnitError::new(operation_name) + ) }); } } diff --git a/impl/src/not_like.rs b/impl/src/not_like.rs index e224f4fe..dc83f3aa 100644 --- a/impl/src/not_like.rs +++ b/impl/src/not_like.rs @@ -145,9 +145,12 @@ fn enum_output_type_and_content( matches.push(matcher); } Fields::Unit => { - let message = format!("Cannot {method_ident}() unit variants"); - matches - .push(quote! { #subtype => ::core::result::Result::Err(#message) }); + let operation_name = method_ident.to_string(); + matches.push(quote! { + #subtype => ::core::result::Result::Err( + ::derive_more::OpsUnitError::new(operation_name) + ) + }); } } } @@ -159,7 +162,7 @@ fn enum_output_type_and_content( }; let output_type = if has_unit_type { - quote! { ::core::result::Result<#input_type #ty_generics, &'static str> } + quote! { ::core::result::Result<#input_type #ty_generics, ::derive_more::OpsUnitError> } } else { quote! { #input_type #ty_generics } }; diff --git a/src/errors/mod.rs b/src/errors/mod.rs new file mode 100644 index 00000000..c421a1d1 --- /dev/null +++ b/src/errors/mod.rs @@ -0,0 +1,4 @@ +#[cfg(feature = "try_into")] +pub(crate) mod try_into; +#[cfg(any(feature = "add", feature = "not"))] +pub(crate) mod ops; diff --git a/src/errors/ops.rs b/src/errors/ops.rs new file mode 100644 index 00000000..563d2535 --- /dev/null +++ b/src/errors/ops.rs @@ -0,0 +1,32 @@ +use core::fmt; + +/// Error returned by the derived implementations when an arithmetic or logic +/// operation is invoked on a unit-like variant of an enum. +#[derive(Clone, Copy, Debug)] +pub struct UnitError { + operation_name: &'static str, +} + +impl UnitError { + /// Creates a new [`OpsUnitError`]. + /// + /// [`OpsUnitError`]: crate::OpsUnitError + #[must_use] + #[inline] + pub const fn new( + operation_name: &'static str, + ) -> Self { + Self { + operation_name + } + } +} + +impl fmt::Display for UnitError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Cannot {}() unit variants", self.operation_name) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for UnitError {} diff --git a/src/errors.rs b/src/errors/try_into.rs similarity index 100% rename from src/errors.rs rename to src/errors/try_into.rs diff --git a/src/lib.rs b/src/lib.rs index cd954dd9..5bb1eba4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,9 @@ #[doc(inline)] pub use derive_more_impl::*; -#[cfg(feature = "try_into")] +#[cfg(any(feature = "add", feature = "not", feature = "try_into"))] mod errors; #[cfg(feature = "try_into")] -pub use crate::errors::TryIntoError; +pub use self::errors::try_into::TryIntoError; +#[cfg(any(feature = "add", feature = "not"))] +pub use self::errors::{ops::UnitError as OpsUnitError}; From 25b5fed7e778fec9616fcef94d0dcc512201ae8e Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 25 Jan 2023 20:29:11 +0200 Subject: [PATCH 2/6] Restructure helper types --- impl/src/add_like.rs | 2 +- impl/src/not_like.rs | 4 ++-- src/{errors/try_into.rs => convert.rs} | 2 ++ src/errors/mod.rs | 4 ---- src/lib.rs | 9 +++++---- src/{errors => }/ops.rs | 2 ++ 6 files changed, 12 insertions(+), 11 deletions(-) rename src/{errors/try_into.rs => convert.rs} (93%) delete mode 100644 src/errors/mod.rs rename src/{errors => }/ops.rs (91%) diff --git a/impl/src/add_like.rs b/impl/src/add_like.rs index 44015247..e291ded2 100644 --- a/impl/src/add_like.rs +++ b/impl/src/add_like.rs @@ -124,7 +124,7 @@ fn enum_content( let operation_name = method_ident.to_string(); matches.push(quote! { (#subtype, #subtype) => ::core::result::Result::Err( - ::derive_more::OpsUnitError::new(operation_name) + ::derive_more::ops::UnitError::new(operation_name) ) }); } diff --git a/impl/src/not_like.rs b/impl/src/not_like.rs index dc83f3aa..99b80a3d 100644 --- a/impl/src/not_like.rs +++ b/impl/src/not_like.rs @@ -148,7 +148,7 @@ fn enum_output_type_and_content( let operation_name = method_ident.to_string(); matches.push(quote! { #subtype => ::core::result::Result::Err( - ::derive_more::OpsUnitError::new(operation_name) + ::derive_more::ops::UnitError::new(operation_name) ) }); } @@ -162,7 +162,7 @@ fn enum_output_type_and_content( }; let output_type = if has_unit_type { - quote! { ::core::result::Result<#input_type #ty_generics, ::derive_more::OpsUnitError> } + quote! { ::core::result::Result<#input_type #ty_generics, ::derive_more::ops::UnitError> } } else { quote! { #input_type #ty_generics } }; diff --git a/src/errors/try_into.rs b/src/convert.rs similarity index 93% rename from src/errors/try_into.rs rename to src/convert.rs index ab3fb281..322286d3 100644 --- a/src/errors/try_into.rs +++ b/src/convert.rs @@ -1,3 +1,5 @@ +//! Definitions used in derived implementations of [`core::convert`] traits. + use core::fmt; /// Error returned by the derived [`TryInto`] implementation. diff --git a/src/errors/mod.rs b/src/errors/mod.rs deleted file mode 100644 index c421a1d1..00000000 --- a/src/errors/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[cfg(feature = "try_into")] -pub(crate) mod try_into; -#[cfg(any(feature = "add", feature = "not"))] -pub(crate) mod ops; diff --git a/src/lib.rs b/src/lib.rs index 5bb1eba4..86ab1b1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,9 +35,10 @@ #[doc(inline)] pub use derive_more_impl::*; -#[cfg(any(feature = "add", feature = "not", feature = "try_into"))] -mod errors; #[cfg(feature = "try_into")] -pub use self::errors::try_into::TryIntoError; +mod convert; +#[cfg(feature = "try_into")] +pub use self::convert::TryIntoError; + #[cfg(any(feature = "add", feature = "not"))] -pub use self::errors::{ops::UnitError as OpsUnitError}; +pub mod ops; diff --git a/src/errors/ops.rs b/src/ops.rs similarity index 91% rename from src/errors/ops.rs rename to src/ops.rs index 563d2535..7f2bf365 100644 --- a/src/errors/ops.rs +++ b/src/ops.rs @@ -1,3 +1,5 @@ +//! Definitions used in derived implementations of [`core::ops`] traits. + use core::fmt; /// Error returned by the derived implementations when an arithmetic or logic From 901d820e0b4668d732cda710026854b1bc89df09 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 25 Jan 2023 20:39:57 +0200 Subject: [PATCH 3/6] Add `ops::WrongVariantError` --- impl/src/add_like.rs | 8 ++++++-- src/ops.rs | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/impl/src/add_like.rs b/impl/src/add_like.rs index e291ded2..19b263bb 100644 --- a/impl/src/add_like.rs +++ b/impl/src/add_like.rs @@ -134,8 +134,12 @@ fn enum_content( if data_enum.variants.len() > 1 { // In the strange case where there's only one enum variant this is would be an unreachable // match. - let message = format!("Trying to {method_ident} mismatched enum variants"); - matches.push(quote! { _ => ::core::result::Result::Err(#message) }); + let operation_name = method_ident.to_string(); + matches.push(quote! { + _ => ::core::result::Result::Err( + ::derive_more::ops::WrongVariantError::new(operation_name) + ) + }); } quote! { match (self, rhs) { diff --git a/src/ops.rs b/src/ops.rs index 7f2bf365..b4ad07d4 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -10,10 +10,7 @@ pub struct UnitError { } impl UnitError { - /// Creates a new [`OpsUnitError`]. - /// - /// [`OpsUnitError`]: crate::OpsUnitError - #[must_use] + #[doc(hidden)] #[inline] pub const fn new( operation_name: &'static str, @@ -32,3 +29,33 @@ impl fmt::Display for UnitError { #[cfg(feature = "std")] impl std::error::Error for UnitError {} + +#[cfg(feature = "add")] +/// Error returned by the derived implementations when an arithmetic or logic +/// operation is invoked on mismatched enum variants. +pub struct WrongVariantError { + operation_name: &'static str, +} + +#[cfg(feature = "add")] +impl WrongVariantError { + #[doc(hidden)] + #[inline] + pub const fn new( + operation_name: &'static str, + ) -> Self { + Self { + operation_name + } + } +} + +#[cfg(feature = "add")] +impl fmt::Display for WrongVariantError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Trying to {}() mismatched enum variants", self.operation_name) + } +} + +#[cfg(all(feature = "add", feature = "std"))] +impl std::error::Error for WrongVariantError {} From 1b164c850b100de029460bab19a4a22dddfeb757 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 25 Jan 2023 20:49:50 +0200 Subject: [PATCH 4/6] Add `ops::BinaryError` --- impl/src/add_like.rs | 12 ++++++---- src/ops.rs | 54 +++++++++++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/impl/src/add_like.rs b/impl/src/add_like.rs index 19b263bb..d13df8cc 100644 --- a/impl/src/add_like.rs +++ b/impl/src/add_like.rs @@ -31,7 +31,9 @@ pub fn expand(input: &DeriveInput, trait_name: &str) -> TokenStream { _ => panic!("Unit structs cannot use derive({trait_name})"), }, Data::Enum(ref data_enum) => ( - quote! { ::core::result::Result<#input_type #ty_generics, &'static str> }, + quote! { + ::core::result::Result<#input_type #ty_generics, ::derive_more::ops::BinaryError> + }, enum_content(input_type, data_enum, &method_ident), ), @@ -124,7 +126,9 @@ fn enum_content( let operation_name = method_ident.to_string(); matches.push(quote! { (#subtype, #subtype) => ::core::result::Result::Err( - ::derive_more::ops::UnitError::new(operation_name) + ::derive_more::ops::BinaryError::Unit( + ::derive_more::ops::UnitError::new(operation_name) + ) ) }); } @@ -136,9 +140,9 @@ fn enum_content( // match. let operation_name = method_ident.to_string(); matches.push(quote! { - _ => ::core::result::Result::Err( + _ => ::core::result::Result::Err(::derive_more::ops::BinaryError::Mismatch( ::derive_more::ops::WrongVariantError::new(operation_name) - ) + )) }); } quote! { diff --git a/src/ops.rs b/src/ops.rs index b4ad07d4..e9cc7d54 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -12,12 +12,8 @@ pub struct UnitError { impl UnitError { #[doc(hidden)] #[inline] - pub const fn new( - operation_name: &'static str, - ) -> Self { - Self { - operation_name - } + pub const fn new(operation_name: &'static str) -> Self { + Self { operation_name } } } @@ -41,21 +37,53 @@ pub struct WrongVariantError { impl WrongVariantError { #[doc(hidden)] #[inline] - pub const fn new( - operation_name: &'static str, - ) -> Self { - Self { - operation_name - } + pub const fn new(operation_name: &'static str) -> Self { + Self { operation_name } } } #[cfg(feature = "add")] impl fmt::Display for WrongVariantError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Trying to {}() mismatched enum variants", self.operation_name) + write!( + f, + "Trying to {}() mismatched enum variants", + self.operation_name + ) } } #[cfg(all(feature = "add", feature = "std"))] impl std::error::Error for WrongVariantError {} + +#[cfg(feature = "add")] +/// Possible errors returned by the derived implementations of binary +/// arithmetic or logic operations. +#[derive(Clone, Copy, Debug)] +pub enum BinaryError { + /// Operation is attempted between mismatched enum variants. + Mismatch(WrongVariantError), + + /// Operation is attempted on unit-like enum variants. + Unit(UnitError), +} + +#[cfg(feature = "add")] +impl fmt::Display for BinaryError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Mismatch(e) => write!(f, "{e}"), + Self::Unit(e) => write!(f, "{e}"), + } + } +} + +#[cfg(all(feature = "add", feature = "std"))] +impl std::error::Error for BinaryError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Mismatch(e) => e.source(), + Self::Unit(e) => e.source(), + } + } +} From c3bfffdfc57954e467dfcdbbcb7adaeee2ca0e89 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 25 Jan 2023 20:58:57 +0200 Subject: [PATCH 5/6] Polish --- impl/src/add_like.rs | 4 ++-- impl/src/not_like.rs | 2 +- src/convert.rs | 2 +- src/ops.rs | 3 +++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/impl/src/add_like.rs b/impl/src/add_like.rs index d13df8cc..7427a39c 100644 --- a/impl/src/add_like.rs +++ b/impl/src/add_like.rs @@ -127,7 +127,7 @@ fn enum_content( matches.push(quote! { (#subtype, #subtype) => ::core::result::Result::Err( ::derive_more::ops::BinaryError::Unit( - ::derive_more::ops::UnitError::new(operation_name) + ::derive_more::ops::UnitError::new(#operation_name) ) ) }); @@ -141,7 +141,7 @@ fn enum_content( let operation_name = method_ident.to_string(); matches.push(quote! { _ => ::core::result::Result::Err(::derive_more::ops::BinaryError::Mismatch( - ::derive_more::ops::WrongVariantError::new(operation_name) + ::derive_more::ops::WrongVariantError::new(#operation_name) )) }); } diff --git a/impl/src/not_like.rs b/impl/src/not_like.rs index 99b80a3d..be4a84fa 100644 --- a/impl/src/not_like.rs +++ b/impl/src/not_like.rs @@ -148,7 +148,7 @@ fn enum_output_type_and_content( let operation_name = method_ident.to_string(); matches.push(quote! { #subtype => ::core::result::Result::Err( - ::derive_more::ops::UnitError::new(operation_name) + ::derive_more::ops::UnitError::new(#operation_name) ) }); } diff --git a/src/convert.rs b/src/convert.rs index 322286d3..4885ace2 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -17,7 +17,7 @@ pub struct TryIntoError { } impl TryIntoError { - /// Creates a new [`TryIntoError`]. + #[doc(hidden)] #[must_use] #[inline] pub const fn new( diff --git a/src/ops.rs b/src/ops.rs index e9cc7d54..fbac0e61 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -11,6 +11,7 @@ pub struct UnitError { impl UnitError { #[doc(hidden)] + #[must_use] #[inline] pub const fn new(operation_name: &'static str) -> Self { Self { operation_name } @@ -29,6 +30,7 @@ impl std::error::Error for UnitError {} #[cfg(feature = "add")] /// Error returned by the derived implementations when an arithmetic or logic /// operation is invoked on mismatched enum variants. +#[derive(Clone, Copy, Debug)] pub struct WrongVariantError { operation_name: &'static str, } @@ -36,6 +38,7 @@ pub struct WrongVariantError { #[cfg(feature = "add")] impl WrongVariantError { #[doc(hidden)] + #[must_use] #[inline] pub const fn new(operation_name: &'static str) -> Self { Self { operation_name } From bb84613fc3a572f17923d20019440450437448e9 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 25 Jan 2023 21:14:04 +0200 Subject: [PATCH 6/6] Upd docs --- CHANGELOG.md | 5 +++-- impl/doc/add.md | 12 ++++++++---- impl/doc/not.md | 8 ++++---- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 425f719f..86fe2e32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - The `From` derive doesn't derive `From<()>` for enum variants without any fields anymore. This feature was removed because it was considered useless in practice. -- The `TryFrom` derive now returns a dedicated error type instead of a - `&'static str` on error. +- The `TryFrom`, `Add`, `Sub`, `BitAnd`, `BitOr`, `BitXor`, `Not` and `Neg` + derives now return a dedicated error type instead of a `&'static str` on + error. - The `Display` derive (and other `fmt`-like ones) now uses `#[display("...", (),*)]` syntax instead of `#[display(fmt = "...", (""),*)]`, and `#[display(bound())]` diff --git a/impl/doc/add.md b/impl/doc/add.md index 80ba4f92..5d58996a 100644 --- a/impl/doc/add.md +++ b/impl/doc/add.md @@ -113,8 +113,8 @@ Code like this will be generated: # Unit, # } impl ::core::ops::Add for MixedInts { - type Output = Result; - fn add(self, rhs: MixedInts) -> Result { + type Output = Result; + fn add(self, rhs: MixedInts) -> Result { match (self, rhs) { (MixedInts::SmallInt(__l_0), MixedInts::SmallInt(__r_0)) => { Ok(MixedInts::SmallInt(__l_0.add(__r_0))) @@ -138,8 +138,12 @@ impl ::core::ops::Add for MixedInts { (MixedInts::UnsignedTwo(__l_0), MixedInts::UnsignedTwo(__r_0)) => { Ok(MixedInts::UnsignedTwo(__l_0.add(__r_0))) } - (MixedInts::Unit, MixedInts::Unit) => Err("Cannot add() unit variants"), - _ => Err("Trying to add mismatched enum variants"), + (MixedInts::Unit, MixedInts::Unit) => Err(::derive_more::ops::BinaryError::Unit( + ::derive_more::ops::UnitError::new("add"), + )), + _ => Err(::derive_more::ops::BinaryError::Mismatch( + ::derive_more::ops::WrongVariantError::new("add"), + )), } } } diff --git a/impl/doc/not.md b/impl/doc/not.md index 4b32d92b..9997c085 100644 --- a/impl/doc/not.md +++ b/impl/doc/not.md @@ -1,6 +1,6 @@ # What `#[derive(Not)]` generates -The derived `Not` implementation simply negates all of the fields of a +The derived `Not` implementation simply negates all the fields of a struct and returns that as a new instance of the struct. For enums all fields of the active variant of the enum are negated and a new instance of the same variant with these negated fields is returned. @@ -148,11 +148,11 @@ Code like this will be generated: # Unit, # } impl ::core::ops::Not for EnumWithUnit { - type Output = Result; - fn not(self) -> Result { + type Output = Result; + fn not(self) -> Result { match self { EnumWithUnit::SmallInt(__0) => Ok(EnumWithUnit::SmallInt(__0.not())), - EnumWithUnit::Unit => Err("Cannot not() unit variants"), + EnumWithUnit::Unit => Err(::derive_more::ops::UnitError::new("not")), } } }