diff --git a/Cargo.lock b/Cargo.lock index 87fd0b3..0df61bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,7 +10,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "loose_enum" -version = "0.1.0-beta.2" +version = "0.1.0" dependencies = [ "num-traits", "serde_core", diff --git a/Cargo.toml b/Cargo.toml index 6a23d95..e5a9d63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "loose_enum" -version = "0.1.0-beta.2" +version = "0.1.0" edition = "2024" description = "A macro for defining loose repr enums." categories = ["no-std", "no-std::no-alloc", "rust-patterns"] diff --git a/README.md b/README.md index 4262b8d..07b61a8 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ When parsing userdata, you often have known/supported cases; however, users don' One way to solve this is having a backup `Undefined` case that supports any value. This crate hopes to simplify this process. ### Example: -For example, an integer repr bool, with 0 being false and 1 being true would look something like this: +An integer repr bool, with 0 being false and 1 being true would look something like this: ```rust loose_enum::loose_enum! { #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)] @@ -20,8 +20,8 @@ loose_enum::loose_enum! { } } ``` -Which expands into the following: -```rust ignore +Which expands into this type: +```rust #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum LooseBool { #[default] @@ -30,11 +30,24 @@ pub enum LooseBool { /// Any value that doesn't match another case. Undefined(i32), } +``` +The macro will also generate `From` and `Into` trait implementations. +If the `serde` feature is enabled, `Serialize` and `Deserialize` will also be implemented. +## Special Cases +### String +A special case has been created for when `String` is the data type, which will implement `From<&str>` on top of the normal trait implementations. +### Generics +There is also **experimental** support for generic types. +```rust +use num_traits::{ConstZero, ConstOne}; -impl From for LooseBool { /* ... */ } -impl From for i32 { /* ... */ } - -// If feature flag `serde` is enabled: -impl Serialize for LooseBool { /* ... */ } -impl Deserialize for LooseBool { /* ... */ } -``` \ No newline at end of file +loose_enum::loose_enum! { + #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)] + pub enum LooseBool { + #[default] + False = T::ZERO, + True = T::ONE, + } +} +``` +Due to the orphan rule, `Into` cannot be implemented. Instead, an `into_repr` method will be added. \ No newline at end of file diff --git a/examples/loose_bool_generic.rs b/examples/loose_bool_generic.rs index 22d80e8..cdd9bcc 100644 --- a/examples/loose_bool_generic.rs +++ b/examples/loose_bool_generic.rs @@ -6,12 +6,12 @@ use core::error::Error; use core::fmt::{Display, Formatter}; use loose_enum::loose_enum; -use num_traits::{ConstOne, ConstZero, PrimInt}; +use num_traits::{ConstOne, ConstZero}; loose_enum! { /// An integer repr bool, with 0 being false and 1 being true. Any other value will be saved as Undefined. #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)] - pub enum LooseBool { + pub enum LooseBool { /// A falsy value of zero. #[default] False = T::ZERO, @@ -20,7 +20,7 @@ loose_enum! { } } -impl LooseBool { +impl LooseBool { /// Returns true if the value is [`True`](Self::True). pub fn is_true(&self) -> bool { matches!(self, Self::True) @@ -40,7 +40,7 @@ impl LooseBool { } } -impl TryFrom> for bool { +impl TryFrom> for bool { type Error = UndefinedBoolError; fn try_from(value: LooseBool) -> Result { diff --git a/macro_docs.md b/macro_docs.md new file mode 100644 index 0000000..078d262 --- /dev/null +++ b/macro_docs.md @@ -0,0 +1,48 @@ +A macro for defining loose repr enums. + +When parsing userdata, you often have known/supported cases; however, users don't always follow the rules. +One way to solve this is having a backup `Undefined` case that supports any value. This macro hopes to simplify this process. + +### Example: +An integer repr bool, with 0 being false and 1 being true would look something like this: +```rust +loose_enum::loose_enum! { + #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)] + pub enum LooseBool: i32 { + #[default] + False = 0, + True = 1, + } +} +``` +Which expands into this type: +```rust +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub enum LooseBool { + #[default] + False, + True, + /// Any value that doesn't match another case. + Undefined(i32), +} +``` +The macro will also generate `From` and `Into` trait implementations. +If the `serde` feature is enabled, `Serialize` and `Deserialize` will also be implemented. +## Special Cases +### String +A special case has been created for when `String` is the data type, which will implement `From<&str>` on top of the normal trait implementations. +### Generics +There is also **experimental** support for generic types. +```rust +use num_traits::{ConstZero, ConstOne}; + +loose_enum::loose_enum! { + #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)] + pub enum LooseBool { + #[default] + False = T::ZERO, + True = T::ONE, + } +} +``` +Due to the orphan rule, `Into` cannot be implemented. Instead, an `into_repr` method will be added. \ No newline at end of file diff --git a/src/__internal.rs b/src/__internal.rs index 28e1890..9101e8a 100644 --- a/src/__internal.rs +++ b/src/__internal.rs @@ -1,5 +1,6 @@ +#[doc(hidden)] #[macro_export] -macro_rules! loose_enum_type { +macro_rules! __loose_enum_type { // All types (including String): ( $(#[$outer:meta])* @@ -45,8 +46,9 @@ macro_rules! loose_enum_type { }; } +#[doc(hidden)] #[macro_export] -macro_rules! loose_enum_impl { +macro_rules! __loose_enum_impl { // Special case for strings: ( $(#[$outer:meta])* @@ -156,8 +158,9 @@ macro_rules! loose_enum_impl { pub use serde_core as serde; #[cfg(feature = "serde")] +#[doc(hidden)] #[macro_export] -macro_rules! loose_enum_serde { +macro_rules! __loose_enum_serde { // Special case for strings: ( $(#[$outer:meta])* diff --git a/src/lib.rs b/src/lib.rs index 454505f..6dc8e77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,8 @@ #[doc(hidden)] pub mod __internal; -/// Defines a repr enum that supports any value. If a value does not match any case, it will be parsed as `Undefined`. #[cfg(not(feature = "serde"))] +#[doc = include_str!("../macro_docs.md")] #[macro_export] macro_rules! loose_enum { // Special case for strings: @@ -15,14 +15,14 @@ macro_rules! loose_enum { $($body:tt)* } ) => { - $crate::loose_enum_type! { + $crate::__loose_enum_type! { $(#[$outer])* $vis enum $name: String { $($body)* } } - $crate::loose_enum_impl! { + $crate::__loose_enum_impl! { $(#[$outer])* $vis enum $name: String { $($body)* @@ -39,14 +39,14 @@ macro_rules! loose_enum { $($body:tt)* } ) => { - $crate::loose_enum_type! { + $crate::__loose_enum_type! { $(#[$outer])* $vis enum $name: $ty { $($body)* } } - $crate::loose_enum_impl! { + $crate::__loose_enum_impl! { $(#[$outer])* $vis enum $name: $ty { $($body)* @@ -64,14 +64,14 @@ macro_rules! loose_enum { $($body:tt)* } ) => { - $crate::loose_enum_type! { + $crate::__loose_enum_type! { $(#[$outer])* $vis enum $name<$ty $( : $first_bound $(+ $other_bounds)* )?> { $($body)* } } - $crate::loose_enum_impl! { + $crate::__loose_enum_impl! { $(#[$outer])* $vis enum $name<$ty $( : $first_bound $(+ $other_bounds)* )?> { $($body)* @@ -82,6 +82,7 @@ macro_rules! loose_enum { /// Defines a repr enum that supports any value. If a value does not match any case, it will be parsed as `Undefined`. #[cfg(feature = "serde")] +#[doc = include_str!("../macro_docs.md")] #[macro_export] macro_rules! loose_enum { // Special case for strings: @@ -91,21 +92,21 @@ macro_rules! loose_enum { $($body:tt)* } ) => { - $crate::loose_enum_type! { + $crate::__loose_enum_type! { $(#[$outer])* $vis enum $name: String { $($body)* } } - $crate::loose_enum_impl! { + $crate::__loose_enum_impl! { $(#[$outer])* $vis enum $name: String { $($body)* } } - $crate::loose_enum_serde! { + $crate::__loose_enum_serde! { $(#[$outer])* $vis enum $name: String { $($body)* @@ -122,21 +123,21 @@ macro_rules! loose_enum { $($body:tt)* } ) => { - $crate::loose_enum_type! { + $crate::__loose_enum_type! { $(#[$outer])* $vis enum $name: $ty { $($body)* } } - $crate::loose_enum_impl! { + $crate::__loose_enum_impl! { $(#[$outer])* $vis enum $name: $ty { $($body)* } } - $crate::loose_enum_serde! { + $crate::__loose_enum_serde! { $(#[$outer])* $vis enum $name: $ty { $($body)* @@ -154,21 +155,21 @@ macro_rules! loose_enum { $($body:tt)* } ) => { - $crate::loose_enum_type! { + $crate::__loose_enum_type! { $(#[$outer])* $vis enum $name<$ty $( : $first_bound $(+ $other_bounds)* )?> { $($body)* } } - $crate::loose_enum_impl! { + $crate::__loose_enum_impl! { $(#[$outer])* $vis enum $name<$ty $( : $first_bound $(+ $other_bounds)* )?> { $($body)* } } - $crate::loose_enum_serde! { + $crate::__loose_enum_serde! { $(#[$outer])* $vis enum $name<$ty $( : $first_bound $(+ $other_bounds)* )?> { $($body)* @@ -179,8 +180,6 @@ macro_rules! loose_enum { #[cfg(test)] mod tests { - use super::*; - #[cfg(feature = "std")] loose_enum!( #[derive(Debug, Eq, PartialEq)]