Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down
33 changes: 23 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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]
Expand All @@ -30,11 +30,24 @@ pub enum LooseBool {
/// Any value that doesn't match another case.
Undefined(i32),
}
```
The macro will also generate `From<i32>` and `Into<i32>` 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<i32> for LooseBool { /* ... */ }
impl From<LooseBool> for i32 { /* ... */ }

// If feature flag `serde` is enabled:
impl Serialize for LooseBool { /* ... */ }
impl Deserialize for LooseBool { /* ... */ }
```
loose_enum::loose_enum! {
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum LooseBool<T: PartialEq + ConstZero + ConstOne> {
#[default]
False = T::ZERO,
True = T::ONE,
}
}
```
Due to the orphan rule, `Into<T>` cannot be implemented. Instead, an `into_repr` method will be added.
8 changes: 4 additions & 4 deletions examples/loose_bool_generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: PrimInt + ConstZero + ConstOne> {
pub enum LooseBool<T: PartialEq + ConstZero + ConstOne> {
/// A falsy value of zero.
#[default]
False = T::ZERO,
Expand All @@ -20,7 +20,7 @@ loose_enum! {
}
}

impl<T: PrimInt + ConstZero + ConstOne> LooseBool<T> {
impl<T: PartialEq + ConstZero + ConstOne> LooseBool<T> {
/// Returns true if the value is [`True`](Self::True).
pub fn is_true(&self) -> bool {
matches!(self, Self::True)
Expand All @@ -40,7 +40,7 @@ impl<T: PrimInt + ConstZero + ConstOne> LooseBool<T> {
}
}

impl<T: PrimInt + ConstZero + ConstOne> TryFrom<LooseBool<T>> for bool {
impl<T: PartialEq + ConstZero + ConstOne> TryFrom<LooseBool<T>> for bool {
type Error = UndefinedBoolError;

fn try_from(value: LooseBool<T>) -> Result<Self, Self::Error> {
Expand Down
48 changes: 48 additions & 0 deletions macro_docs.md
Original file line number Diff line number Diff line change
@@ -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<i32>` and `Into<i32>` 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<T: PartialEq + ConstZero + ConstOne> {
#[default]
False = T::ZERO,
True = T::ONE,
}
}
```
Due to the orphan rule, `Into<T>` cannot be implemented. Instead, an `into_repr` method will be added.
9 changes: 6 additions & 3 deletions src/__internal.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#[doc(hidden)]
#[macro_export]
macro_rules! loose_enum_type {
macro_rules! __loose_enum_type {
// All types (including String):
(
$(#[$outer:meta])*
Expand Down Expand Up @@ -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])*
Expand Down Expand Up @@ -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])*
Expand Down
35 changes: 17 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)*
Expand All @@ -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)*
Expand All @@ -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)*
Expand All @@ -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:
Expand All @@ -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)*
Expand All @@ -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)*
Expand All @@ -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)*
Expand All @@ -179,8 +180,6 @@ macro_rules! loose_enum {

#[cfg(test)]
mod tests {
use super::*;

#[cfg(feature = "std")]
loose_enum!(
#[derive(Debug, Eq, PartialEq)]
Expand Down