Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow implementing core::error::Error when available #184

Merged
merged 3 commits into from
Nov 9, 2024

Conversation

vic1707
Copy link
Contributor

@vic1707 vic1707 commented Sep 13, 2024

rust 1.81.0 made std::error::Error available in core.
Users with that version or higher should be getting the Error impl no matter if std feature is enabled.
fixes #179 without introducing a breaking change

@vic1707
Copy link
Contributor Author

vic1707 commented Sep 13, 2024

I think its good

lib.rs

use nutype::nutype;

#[nutype(validate(finite))]
struct A(f32);

Cargo.toml

[dependencies]
nutype = { path = "../nutype/nutype" }

Outputs of cargo expand

1.81.0

`nutype = { path = "../nutype/nutype" }`

=> impl ::core::error::Error for AError { /* ... */ }

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use nutype::nutype;
#[doc(hidden)]
mod __nutype_A__ {
    use super::*;
    pub struct A(f32);
    #[allow(clippy::enum_variant_names)]
    pub enum AError {
        FiniteViolated,
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::fmt::Debug for AError {
        #[inline]
        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
            ::core::fmt::Formatter::write_str(f, "FiniteViolated")
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::clone::Clone for AError {
        #[inline]
        fn clone(&self) -> AError {
            AError::FiniteViolated
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::marker::StructuralPartialEq for AError {}
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::PartialEq for AError {
        #[inline]
        fn eq(&self, other: &AError) -> bool {
            true
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::Eq for AError {
        #[inline]
        #[doc(hidden)]
        #[coverage(off)]
        fn assert_receiver_is_total_eq(&self) -> () {}
    }
    impl ::core::fmt::Display for AError {
        fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
            match self {
                AError::FiniteViolated => {
                    f.write_fmt(format_args!("{0} is not finite.", "A"))
                }
            }
        }
    }
    impl ::core::error::Error for AError {
        fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> {
            None
        }
    }
    impl A {
        pub fn try_new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            let sanitized_value: f32 = Self::__sanitize__(raw_value);
            Self::__validate__(&sanitized_value)?;
            Ok(A(sanitized_value))
        }
        fn __sanitize__(mut value: f32) -> f32 {
            value
        }
        fn __validate__(val: &f32) -> core::result::Result<(), AError> {
            let val = *val;
            if !val.is_finite() {
                return Err(AError::FiniteViolated);
            }
            Ok(())
        }
        #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")]
        pub fn new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            Self::try_new(raw_value)
        }
    }
    impl A {
        #[inline]
        pub fn into_inner(self) -> f32 {
            self.0
        }
    }
}
use __nutype_A__::A;
use __nutype_A__::AError;
`nutype = { path = "../nutype/nutype", default-features = false }`

=> impl ::core::error::Error for AError { /* ... */ }

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use nutype::nutype;
#[doc(hidden)]
mod __nutype_A__ {
    use super::*;
    pub struct A(f32);
    #[allow(clippy::enum_variant_names)]
    pub enum AError {
        FiniteViolated,
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::fmt::Debug for AError {
        #[inline]
        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
            ::core::fmt::Formatter::write_str(f, "FiniteViolated")
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::clone::Clone for AError {
        #[inline]
        fn clone(&self) -> AError {
            AError::FiniteViolated
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::marker::StructuralPartialEq for AError {}
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::PartialEq for AError {
        #[inline]
        fn eq(&self, other: &AError) -> bool {
            true
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::Eq for AError {
        #[inline]
        #[doc(hidden)]
        #[coverage(off)]
        fn assert_receiver_is_total_eq(&self) -> () {}
    }
    impl ::core::fmt::Display for AError {
        fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
            match self {
                AError::FiniteViolated => {
                    f.write_fmt(format_args!("{0} is not finite.", "A"))
                }
            }
        }
    }
    impl ::core::error::Error for AError {
        fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> {
            None
        }
    }
    impl A {
        pub fn try_new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            let sanitized_value: f32 = Self::__sanitize__(raw_value);
            Self::__validate__(&sanitized_value)?;
            Ok(A(sanitized_value))
        }
        fn __sanitize__(mut value: f32) -> f32 {
            value
        }
        fn __validate__(val: &f32) -> core::result::Result<(), AError> {
            let val = *val;
            if !val.is_finite() {
                return Err(AError::FiniteViolated);
            }
            Ok(())
        }
        #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")]
        pub fn new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            Self::try_new(raw_value)
        }
    }
    impl A {
        #[inline]
        pub fn into_inner(self) -> f32 {
            self.0
        }
    }
}
use __nutype_A__::A;
use __nutype_A__::AError;

1.80.0

`nutype = { path = "../nutype/nutype" }`

=> impl ::std::error::Error for AError { /* ... */ }

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use nutype::nutype;
#[doc(hidden)]
mod __nutype_A__ {
    use super::*;
    pub struct A(f32);
    #[allow(clippy::enum_variant_names)]
    pub enum AError {
        FiniteViolated,
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::fmt::Debug for AError {
        #[inline]
        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
            ::core::fmt::Formatter::write_str(f, "FiniteViolated")
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::clone::Clone for AError {
        #[inline]
        fn clone(&self) -> AError {
            AError::FiniteViolated
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::marker::StructuralPartialEq for AError {}
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::PartialEq for AError {
        #[inline]
        fn eq(&self, other: &AError) -> bool {
            true
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::Eq for AError {
        #[inline]
        #[doc(hidden)]
        #[coverage(off)]
        fn assert_receiver_is_total_eq(&self) -> () {}
    }
    impl ::core::fmt::Display for AError {
        fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
            match self {
                AError::FiniteViolated => {
                    f.write_fmt(format_args!("{0} is not finite.", "A"))
                }
            }
        }
    }
    impl ::std::error::Error for AError {
        fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
            None
        }
    }
    impl A {
        pub fn try_new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            let sanitized_value: f32 = Self::__sanitize__(raw_value);
            Self::__validate__(&sanitized_value)?;
            Ok(A(sanitized_value))
        }
        fn __sanitize__(mut value: f32) -> f32 {
            value
        }
        fn __validate__(val: &f32) -> core::result::Result<(), AError> {
            let val = *val;
            if !val.is_finite() {
                return Err(AError::FiniteViolated);
            }
            Ok(())
        }
        #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")]
        pub fn new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            Self::try_new(raw_value)
        }
    }
    impl A {
        #[inline]
        pub fn into_inner(self) -> f32 {
            self.0
        }
    }
}
use __nutype_A__::A;
use __nutype_A__::AError;
`nutype = { path = "../nutype/nutype", default-features = false }`

=> No implementation of Error

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use nutype::nutype;
#[doc(hidden)]
mod __nutype_A__ {
    use super::*;
    pub struct A(f32);
    #[allow(clippy::enum_variant_names)]
    pub enum AError {
        FiniteViolated,
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::fmt::Debug for AError {
        #[inline]
        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
            ::core::fmt::Formatter::write_str(f, "FiniteViolated")
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::clone::Clone for AError {
        #[inline]
        fn clone(&self) -> AError {
            AError::FiniteViolated
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::marker::StructuralPartialEq for AError {}
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::PartialEq for AError {
        #[inline]
        fn eq(&self, other: &AError) -> bool {
            true
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::Eq for AError {
        #[inline]
        #[doc(hidden)]
        #[coverage(off)]
        fn assert_receiver_is_total_eq(&self) -> () {}
    }
    impl ::core::fmt::Display for AError {
        fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
            match self {
                AError::FiniteViolated => {
                    f.write_fmt(format_args!("{0} is not finite.", "A"))
                }
            }
        }
    }
    impl A {
        pub fn try_new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            let sanitized_value: f32 = Self::__sanitize__(raw_value);
            Self::__validate__(&sanitized_value)?;
            Ok(A(sanitized_value))
        }
        fn __sanitize__(mut value: f32) -> f32 {
            value
        }
        fn __validate__(val: &f32) -> core::result::Result<(), AError> {
            let val = *val;
            if !val.is_finite() {
                return Err(AError::FiniteViolated);
            }
            Ok(())
        }
        #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")]
        pub fn new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            Self::try_new(raw_value)
        }
    }
    impl A {
        #[inline]
        pub fn into_inner(self) -> f32 {
            self.0
        }
    }
}
use __nutype_A__::A;
use __nutype_A__::AError;

1.83.0-nightly

`nutype = { path = "../nutype/nutype" }`

=> impl ::std::error::Error for AError { /* ... */ }

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use nutype::nutype;
#[doc(hidden)]
mod __nutype_A__ {
    use super::*;
    pub struct A(f32);
    #[allow(clippy::enum_variant_names)]
    pub enum AError {
        FiniteViolated,
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::fmt::Debug for AError {
        #[inline]
        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
            ::core::fmt::Formatter::write_str(f, "FiniteViolated")
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::clone::Clone for AError {
        #[inline]
        fn clone(&self) -> AError {
            AError::FiniteViolated
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::marker::StructuralPartialEq for AError {}
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::PartialEq for AError {
        #[inline]
        fn eq(&self, other: &AError) -> bool {
            true
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::Eq for AError {
        #[inline]
        #[doc(hidden)]
        #[coverage(off)]
        fn assert_receiver_is_total_eq(&self) -> () {}
    }
    impl ::core::fmt::Display for AError {
        fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
            match self {
                AError::FiniteViolated => {
                    f.write_fmt(format_args!("{0} is not finite.", "A"))
                }
            }
        }
    }
    impl ::core::error::Error for AError {
        fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> {
            None
        }
    }
    impl A {
        pub fn try_new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            let sanitized_value: f32 = Self::__sanitize__(raw_value);
            Self::__validate__(&sanitized_value)?;
            Ok(A(sanitized_value))
        }
        fn __sanitize__(mut value: f32) -> f32 {
            value
        }
        fn __validate__(val: &f32) -> core::result::Result<(), AError> {
            let val = *val;
            if !val.is_finite() {
                return Err(AError::FiniteViolated);
            }
            Ok(())
        }
        #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")]
        pub fn new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            Self::try_new(raw_value)
        }
    }
    impl A {
        #[inline]
        pub fn into_inner(self) -> f32 {
            self.0
        }
    }
}
use __nutype_A__::A;
use __nutype_A__::AError;
`nutype = { path = "../nutype/nutype", default-features = false }`

=> impl ::core::error::Error for AError { /* ... */ }

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use nutype::nutype;
#[doc(hidden)]
mod __nutype_A__ {
    use super::*;
    pub struct A(f32);
    #[allow(clippy::enum_variant_names)]
    pub enum AError {
        FiniteViolated,
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::fmt::Debug for AError {
        #[inline]
        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
            ::core::fmt::Formatter::write_str(f, "FiniteViolated")
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::clone::Clone for AError {
        #[inline]
        fn clone(&self) -> AError {
            AError::FiniteViolated
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::marker::StructuralPartialEq for AError {}
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::PartialEq for AError {
        #[inline]
        fn eq(&self, other: &AError) -> bool {
            true
        }
    }
    #[automatically_derived]
    #[allow(clippy::enum_variant_names)]
    impl ::core::cmp::Eq for AError {
        #[inline]
        #[doc(hidden)]
        #[coverage(off)]
        fn assert_receiver_is_total_eq(&self) -> () {}
    }
    impl ::core::fmt::Display for AError {
        fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
            match self {
                AError::FiniteViolated => {
                    f.write_fmt(format_args!("{0} is not finite.", "A"))
                }
            }
        }
    }
    impl ::core::error::Error for AError {
        fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> {
            None
        }
    }
    impl A {
        pub fn try_new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            let sanitized_value: f32 = Self::__sanitize__(raw_value);
            Self::__validate__(&sanitized_value)?;
            Ok(A(sanitized_value))
        }
        fn __sanitize__(mut value: f32) -> f32 {
            value
        }
        fn __validate__(val: &f32) -> core::result::Result<(), AError> {
            let val = *val;
            if !val.is_finite() {
                return Err(AError::FiniteViolated);
            }
            Ok(())
        }
        #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")]
        pub fn new(raw_value: f32) -> ::core::result::Result<Self, AError> {
            Self::try_new(raw_value)
        }
    }
    impl A {
        #[inline]
        pub fn into_inner(self) -> f32 {
            self.0
        }
    }
}
use __nutype_A__::A;
use __nutype_A__::AError;

@vic1707 vic1707 marked this pull request as ready for review September 13, 2024 11:25
@greyblake
Copy link
Owner

greyblake commented Sep 17, 2024

@vic1707 Hi thanks for your PRs.
Just to keep you update: sorry for the delay.
I had seen them and wanted to review over the weekend by did not manage to.
I'll try to process them on the next weekend.

@vic1707
Copy link
Contributor Author

vic1707 commented Sep 17, 2024

No problem take your time 👍
Thanks for telling me

@vic1707
Copy link
Contributor Author

vic1707 commented Sep 18, 2024

Added the cargo expand on nightly just to be sure I didn't ruin everything.

@vic1707
Copy link
Contributor Author

vic1707 commented Sep 29, 2024

Hi @greyblake may I ask for an update ?

@greyblake
Copy link
Owner

greyblake commented Sep 29, 2024 via email

@vic1707
Copy link
Contributor Author

vic1707 commented Oct 29, 2024

Hi @greyblake may I ask for an update ?

@greyblake
Copy link
Owner

@vic1707 Sorry, I'll try to merge this on the weekend.

#[allow(unused_variables)]
pub fn gen_impl_error_trait(error_type_name: &ErrorTypeName) -> TokenStream {
cfg_if! {
if #[cfg(feature = "std")] {
if #[cfg(ERROR_IN_CORE)] {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the implementation is the same we should have something like
#[cfg(ERROR_IN_CORE)] || #[cfg(feature = "std")] instead of another else if branch that generates the same code.

If #[cfg(ERROR_IN_CORE)] || #[cfg(feature = "std")] by some reason is not possible, then we should still DRY it up, by extracting the generated code in a var.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not exactly the same implementation due to std and core imports.
I tried to DRY it up but couldn't at the time (partly because I didn't even think of #[cfg(ERROR_IN_CORE)] || #[cfg(feature = "std")]).

I saw a bunch of similar PRs about std vs core in the mean time, I'll give it another go 👍.

Copy link
Contributor Author

@vic1707 vic1707 Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well 😓
image
image
image

And I can't find any infos on how to do an OR operation, reading the source code I don't see any option to do it (but I could've missed it, I'm not really good with macros 😂).
The not() operation doesn't seem to be possible either.

I have another idea, I'll keep you updated !

EDIT: I forgot that the correct syntax is simply #[cfg(any(option1, option2))] 🤦.

@@ -68,27 +68,38 @@ pub fn gen_def_parse_error(
};

cfg_if! {
if #[cfg(feature = "std")] {
if #[cfg(ERROR_IN_CORE)] {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here we should have #[cfg(ERROR_IN_CORE)] || #[cfg(feature = "std")]

@greyblake
Copy link
Owner

@vic1707 Thank you for your contribution and again sorry for the delay.
I've dropped 2 tiny comments. Let me know if you'd like to address them, if no, I guess I can do it.

@vic1707 vic1707 force-pushed the error-in-core branch 2 times, most recently from e5d428a to dbd7b69 Compare November 8, 2024 23:40
@vic1707
Copy link
Contributor Author

vic1707 commented Nov 8, 2024

@greyblake, just pushed a dryer version, what do you think ?

I thought of doing

let error = cfg_if! {
    if #[cfg(ERROR_IN_CORE)] {
        quote! { ::core::error::Error }
    } else {
        quote! { ::std::error::Error }
    }
};

instead of the proposed version but it's not yet supported by rust itself, see: rust-lang/rust#15701.

Also, do you think you could publish a new version after this merge ?

@greyblake
Copy link
Owner

@vic1707 Sorry by bad, in my thinking I assumed that we could just use the core:: variant. With a fresh head, I see that it's obviously would be contra-productive and could not work with the older rust versions.
Thank you very much for the very hard try to DRY it.

@greyblake greyblake merged commit d398ce8 into greyblake:master Nov 9, 2024
6 checks passed
@vic1707 vic1707 deleted the error-in-core branch November 9, 2024 10:32
@vic1707
Copy link
Contributor Author

vic1707 commented Nov 9, 2024

No problem, it helped me get a dryer version out, win-win 👍.
How about releasing 0.5.1 @greyblake ? Since 0.5.0 we got a few fixes and QOL improvements 🙃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Generate core::error::Error for no_std
2 participants