Expand description

EnPow

License

EnPow is a procedural macro crate used to enPower user defined Enums with many methods usually known from the standard library’s Result<T, E> and Option<T>. It can generate methods like fn is_<variant>(&self) -> bool or fn unwrap_<variant>(self) -> <inner>, supporting variants with named or unnamed fields (or none), as well as generics. See the enpow macro documentation for details on the specific methods supported.

Additionally, this crate allows to extract the data associated with each enum variant into separate structs, allowing for more compact code e.g. when designing an Abstract Syntax Tree. See the extract macro documentation for more details.

It is also possible to combine both macros when keeping them in the right order: first extract and then enpow. Combining both macros avoids generating separate structs for Ref or Mut struct variants as demonstrated in one of the following examples.

Usage Examples

The following examples demonstrate the separate usage of the macros enpow and extract, as well as their combined usage. See the macro’s documentation for more details.

Using just enpow

use enpow::enpow;

#[enpow(Var, VarAsRef)]
#[enpow_derive(Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum Token<Span> {
    /// `+`
    Plus(
        /// Source span
        Span
    ),
    /// Unsigned integer literal
    Number {
        /// Source span
        span: Span,
        /// Value
        value: u64,
    }
}

// Use the auto implementations
assert_eq!(Token::Plus(3).plus(), Some(3));
assert_eq!(Token::Plus(7).number(), None);
assert_eq!(Token::Number { span: 0, value: 42 }.number().unwrap().span, 0);

let mut num = Token::Number { span: 10, value: 7 };
*num.number_as_mut().unwrap().span = 20;
assert_eq!(num.number(), Some(TokenNumber { span: 20, value: 7 }))
See generated code
#[derive(Clone, Debug, PartialEq)]
pub enum Token<Span> {
    /// `+`
    Plus(
        /// Source span
        Span
    ),
    /// Unsigned integer literal
    Number {
        /// Source span
        span: Span,
        /// Value
        value: u64,
    }
}

#[allow(unused)]
#[derive(Debug, PartialEq)]
/// Unsigned integer literal
pub struct TokenNumber<Span> {
    /// Source span
    pub span: Span,
    /// Value
    pub value: u64,
}

#[allow(unused)]
#[derive(Debug, PartialEq, Clone, Copy)]
/// Unsigned integer literal
pub struct TokenNumberRef<'token_number, Span> {
    /// Source span
    pub span: &'token_number Span,
    /// Value
    pub value: &'token_number u64,
}

#[allow(unused)]
#[derive(Debug, PartialEq)]
/// Unsigned integer literal
pub struct TokenNumberMut<'token_number, Span> {
    /// Source span
    pub span: &'token_number mut Span,
    /// Value
    pub value: &'token_number mut u64,
}

#[automatically_derived]
#[allow(unused)]
impl<Span> Token<Span> {
    pub fn plus(self) -> Option<Span> {
        match self {
            Token::Plus(f0) => Some(f0),
            _ => None,
        }
    }

    pub fn plus_as_ref(&self) -> Option<&Span> {
        match self {
            Token::Plus(f0) => Some(f0),
            _ => None,
        }
    }

    pub fn plus_as_mut(&mut self) -> Option<&mut Span> {
        match self {
            Token::Plus(f0) => Some(f0),
            _ => None,
        }
    }

    pub fn number(self) -> Option<TokenNumber<Span>> {
        match self {
            Token::Number { span, value } => Some(TokenNumber { span, value }),
            _ => None,
        }
    }

    pub fn number_as_ref(&self) -> Option<TokenNumberRef<Span>> {
        match self {
            Token::Number { span, value } => Some(TokenNumberRef { span, value }),
            _ => None,
        }
    }

    pub fn number_as_mut(&mut self) -> Option<TokenNumberMut<Span>> {
        match self {
            Token::Number { span, value } => Some(TokenNumberMut { span, value }),
            _ => None,
        }
    }
}

// Use the auto implementations
assert_eq!(Token::Plus(3).plus(), Some(3));
assert_eq!(Token::Plus(7).number(), None);
assert_eq!(Token::Number { span: 0, value: 42 }.number().unwrap().span, 0);

let mut num = Token::Number { span: 10, value: 7 };
*num.number_as_mut().unwrap().span = 20;
assert_eq!(num.number(), Some(TokenNumber { span: 20, value: 7 }))

Using just extract

use enpow::extract;

#[extract(All)]
#[extract_derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum Token<Span> {
    /// `+`
    Plus(
        /// Source span
        Span
    ),
    /// Unsigned integer literal
    Number {
        /// Source span
        span: Span,
        /// Value
        value: u64,
    }
}

// Use Debug and PartialEq
assert_eq!(TokenPlus((2,3)), TokenPlus((2,3)));
assert_eq!(
    TokenNumber { span: (0, 4), value: 1024 },
    TokenNumber { span: (0, 4), value: 1024 }
);
See generated code
#[derive(Clone, Debug, PartialEq)]
pub enum Token<Span> {
    /// `+`
    Plus(TokenPlus<Span>),
    /// Unsigned integer literal
    Number(TokenNumber<Span>),
}

#[derive(Clone, Debug, PartialEq)]
/// `+`
pub struct TokenPlus<Span>(
    /// Source span
    pub Span
);

#[derive(Clone, Debug, PartialEq)]
/// Unsigned integer literal
pub struct TokenNumber<Span> {
    /// Source span
    pub span: Span,
    /// Value
    pub value: u64,
}

// Use Debug and PartialEq
assert_eq!(TokenPlus((2,3)), TokenPlus((2,3)));
assert_eq!(
    TokenNumber { span: (0, 4), value: 1024 },
    TokenNumber { span: (0, 4), value: 1024 }
);

Combining extract and enpow

use enpow::{enpow, extract};

#[extract(Named)]
#[extract_derive(Clone, Debug, PartialEq)]
#[enpow(IsVar)]
#[derive(Clone, Debug, PartialEq)]
pub enum Token<Span> {
    /// `+`
    Plus(
        /// Source span
        Span
    ),
    /// Unsigned integer literal
    Number {
        /// Source span
        span: Span,
        /// Value
        value: u64,
    }
}

// Use the auto implementations
let token = Token::Number(TokenNumber { span: (0, 3), value: 1024 });
assert!(token.is_number_and(|num: &TokenNumber<_>| num.value == 1024));
See generated code
#[derive(Clone, Debug, PartialEq)]
pub enum Token<Span> {
    /// `+`
    Plus(
        /// Source span
        Span
    ),
    /// Unsigned integer literal
    Number(TokenNumber<Span>),
}

#[automatically_derived]
#[allow(unused)]
impl<Span> Token<Span> {
    pub fn is_plus(&self) -> bool {
        match self {
            Token::Plus(f0) => true,
            _ => false,
        }
    }

    pub fn is_plus_and(&self, f: impl FnOnce(&Span) -> bool) -> bool {
        match self {
            Token::Plus(f0) => f(f0),
            _ => false,
        }
    }

    pub fn is_number(&self) -> bool {
        match self {
            Token::Number(f0) => true,
            _ => false,
        }
    }

    pub fn is_number_and(&self, f: impl FnOnce(&TokenNumber<Span>) -> bool) -> bool {
        match self {
            Token::Number(f0) => f(f0),
            _ => false,
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
/// Unsigned integer literal
pub struct TokenNumber<Span> {
    /// Source span
    pub span: Span,
    /// Value
    pub value: u64,
}

// Use the auto implementations
let token = Token::Number(TokenNumber { span: (0, 3), value: 1024 });
assert!(token.is_number_and(|num: &TokenNumber<_>| num.value == 1024));

Inspiration

While the first plan for this crate was limited to simple unwrap_as methods and alike, the crate variantly was a great inspiration to take this idea way further. It can be seen as limited alternative to this crate.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as below, without any additional terms or conditions.

License

© 2022 Florian Köhler.

This project is licensed at your option under either of

The SPDX license identifier for this project is MIT OR Apache-2.0.


Licensing derived from arnavyc/dual-licensed-mit-apache

Attribute Macros

The enpow attribute attached to the target enum derives typical methods for working with variants as known from Result<T, E> and Option<T>. It supports generics and variants of every type, with named or unnamed fields or no fields attached. Variants with unnamed fields get unwrapped into a tuple, while variants with named fields are transformed into an automatically generated struct named after the enum and variant, i.e. EnumVariant. The functions and struct generated inherit the visibility modifier of the target enum.

The extract attribute attached to an enum turns each variant into a separate struct that then becomes the only field of the variant. It supports generics and variants of every type, with named or unnamed fields or no fields attached. Variants without data are turned into unit structs, variants with unnamed fields get turned into tuple structs, and variants with named fields are transformed into structs, each named after the enum and variant, i.e. EnumVariant. The structs generated inherit the visibility modifier of the target enum. Additionally, doc comments attached to the variants and variant fields are inherited by the generated structs.