Attribute Macro enpow::enpow

source ·
#[enpow]
Expand description

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.

In parethesis, the following arguments to enpow can be used to specify which methods to generate. Without any arguments, all methods will be generated. The method identifiers are generated from the variant names turned into snake case.

  • Var
    • fn <variant>(self) -> Option<<inner>> Returns the inner data, if the enum value is of the expected type, otherwise returns None.
  • IsVar
    • fn is_<variant>(&self) -> bool Returns true, if the enum value is of the expected type, otherwise returns false.
    • fn is_<variant>_and(&self, f: impl FnOnce(<ref_inner>) -> bool) -> bool Returns true, if the enum value is of the expected type and the given closure evalutates to true, otherwise returns false.
  • VarAsRef
    • fn <variant>_as_ref(&self) -> Option<<ref_inner>> Returns a reference to the inner data, if the enum value is of the expected type, otherwise returns None.
    • fn <variant>_as_mut(&mut self) -> Option<<mut_inner>> Returns a mutable reference to the inner data, if the enum value is of the expected type, otherwise returns None.
  • MapVar
    • fn map_<variant>_or<T>(self, default: T, op: impl FnOnce(<inner>) -> T) -> T Applies the given operation to the inner data and returns its result, if the enum value is of the expected type, otherwise returns the given default value.
    • fn map_<variant>_or_else<T>(self, default: impl FnOnce(Self) -> T, op: impl FnOnce(<inner>) -> T) -> T Applies the given operation to the inner data and returns its result, if the enum value is of the expected type, otherwise returns the value that the given default closure evaluates to.
  • UnwrapVar
    • fn unwrap_<variant>(self) -> <inner> Returns the inner data, if the enum value is of the expected type, otherwise panics.
    • fn unwrap_<variant>_or(self, default: <inner>) -> <inner> Returns the inner data, if the enum value is of the expected type, otherwise returns the given default value.
    • fn unwrap_<variant>_or_else(self, f: impl FnOnce(Self) -> <inner>) -> <inner> Returns the inner data, if the enum value is of the expected type, otherwise returns the value that the given closure evaluated to.
  • UnwrapVar, VarAsRef
    • fn unwrap_<variant>_as_ref(self) -> <ref_inner> Returns a reference to the inner data, if the enum value is of the expected type, otherwise panics.
    • fn unwrap_<variant>_as_mut(self) -> <mut_inner> Returns a mutable reference to the inner data, if the enum value is of the expected type, otherwise panics.
  • ExpectVar
    • fn expect_<variant>(self, msg: &str) -> <inner> Returns the inner data, if the enum is of the expected type, otherwise panics with the given error message.
  • ExpectVar, VarAsRef
    • fn expect_<variant>_as_ref(self, msg: &str) -> <ref_inner> Returns a reference to the inner data, if the enum is of the expected type, otherwise panics with the given error message.
    • fn expect_<variant>_as_mut(self, msg: &str) -> <mut_inner> Returns a mutable reference to the inner data, if the enum is of the expected type, otherwise panics with the given error message.
  • All
    • Generates all methods mentioned.

This example will generate all methods.

#[enpow(All)]
#[inner(derive(Debug, PartialEq))]
#[derive(Debug, PartialEq)]
enum IpAddress {
    None,
    V4(u8, u8, u8, u8),
    V6(String),
    Multi {
        v4: (u8, u8, u8, u8),
        v6: String,
    },
}

// fn <variant>()
assert_eq!(IpAddress::V4(192, 168, 0, 1).v4(), Some((192, 168, 0, 1)));
assert_eq!(IpAddress::V6("::1".into()).v6(), Some("::1".into()));
assert_eq!(IpAddress::None.multi(), None);

// fn is_<variant>()
assert_eq!(IpAddress::None.is_none(), true);
assert_eq!(IpAddress::V6("::1".into()).is_v4(), false);

// fn is_<variant>_and()
assert_eq!(IpAddress::V4(192, 168, 0, 1).is_v4_and(|ip| *ip.0 == 192), true);
assert_eq!(IpAddress::V6("::1".into()).is_v6_and(|ip| *ip == "::"), false);
assert_eq!(IpAddress::None.is_v4_and(|_| true), false);

// fn <variant>_as_ref()
assert_eq!(IpAddress::V4(192, 168, 0, 1).v4_as_ref(), Some((&192, &168, &0, &1)));
assert_eq!(
    IpAddress::Multi { v4: (0, 0, 0, 0), v6: "::".into() }.multi_as_ref(),
    Some(IpAddressMultiRef { v4: &(0, 0, 0, 0), v6: &"::".into() })
);
assert_eq!(IpAddress::V6("::1".into()).none_as_ref(), None);

// fn <variant>_as_mut()
let mut ip = IpAddress::V4(192, 168, 0, 1);
if let Some(v4) = ip.v4_as_mut() {
    *v4.3 = 2;
}
assert_eq!(ip, IpAddress::V4(192, 168, 0, 2));

// fn map_<variant>_or()
assert_eq!(
    IpAddress::V4(192, 168, 0, 1).map_v4_or((0, 0, 0, 0), |mut v4| { v4.3 = 2; v4 }),
    (192, 168, 0, 2)
);
assert_eq!(
    IpAddress::None.map_v6_or("::".into(), |v6| v6),
    "::".to_owned()
);

// fn map_<variant>_or_else()
assert_eq!(
    IpAddress::V6("::".into()).map_v6_or_else(|_| unreachable!(), |v6| v6 + "1"),
    "::1".to_owned()
);
assert_eq!(
    IpAddress::None.map_v4_or_else(|_| (0, 0, 0, 0), |_| unreachable!()),
    (0, 0, 0, 0)
);

// fn unwrap_<variant>()
assert_eq!(IpAddress::V6("::1".into()).unwrap_v6(), "::1".to_owned());

// fn unwrap_<variant>_as_ref()
assert_eq!(IpAddress::V4(192, 168, 0, 1).unwrap_v4_as_ref(), (&192, &168, &0, &1));

// fn unwrap_<variant>_as_mut()
let mut ip = IpAddress::V4(192, 168, 0, 1);
*ip.unwrap_v4_as_mut().3 = 2;
assert_eq!(ip, IpAddress::V4(192, 168, 0, 2));

// fn unwrap_<variant>_or()
assert_eq!(IpAddress::V6("::1".into()).unwrap_v6_or("::".into()), "::1".to_owned());
assert_eq!(IpAddress::V4(192, 168, 0, 2).unwrap_v6_or("::".into()), "::".to_owned());

// fn unwrap_<variant>_or_else()
assert_eq!(IpAddress::None.unwrap_v4_or_else(|_| (0, 0, 0, 0)), (0, 0, 0, 0));
assert_eq!(
    IpAddress::V6("::1".into()).unwrap_v6_or_else(|_| unreachable!()),
    "::1".to_owned()
);

// fn expect_<variant>()
assert_eq!(IpAddress::V4(192, 168, 0, 1).expect_v4("Expected V4"), (192, 168, 0, 1));

// fn unwrap_<variant>_as_ref()
assert_eq!(
    IpAddress::V6("::1".into()).expect_v6_as_ref("Unexpected variant"),
    &"::1".to_owned()
);

// fn unwrap_<variant>_as_mut()
let mut ip = IpAddress::V6("::".into());
ip.expect_v6_as_mut("Expected V6").push('1');
assert_eq!(ip, IpAddress::V6("::1".into()));

This example will generate methods of the category Var and IsVar.

#[enpow(Var, IsVar)]
#[inner(derive(Debug, PartialEq))]
#[derive(Debug, PartialEq)]
enum IpAddress {
    None,
    V4(u8, u8, u8, u8),
    V6(String),
    Multi {
        v4: (u8, u8, u8, u8),
        v6: String,
    },
}

// fn <variant>()
assert_eq!(IpAddress::V4(192, 168, 0, 1).v4(), Some((192, 168, 0, 1)));
assert_eq!(IpAddress::None.multi(), None);

// fn is_<variant>()
assert_eq!(IpAddress::None.is_none(), true);
assert_eq!(IpAddress::V6("::1".into()).is_v4(), false);

// fn is_<variant>_and()
assert_eq!(IpAddress::V4(192, 168, 0, 1).is_v4_and(|ip| *ip.0 == 192), true);
assert_eq!(IpAddress::V6("::1".into()).is_v6_and(|ip| *ip == "::"), false);
assert_eq!(IpAddress::None.is_v4_and(|_| true), false);

Configuration

The helper attribute inner is used to configure the main macros expand and enpow. For this, inner can be attached to both the enum itself and to individual variants. However, it has to be placed after each main macro it should be effective for. An inner macro placed after multiple main macros will be effective for each one of them.

Derives

The argument derive() enables to add auto trait derives to the automatically generated types. The position of the attribute decides on whether the given traits are implemented for all variant types or just for the selected ones.

ℹ️ Ref structs always automatically derive Clone and Copy, while Mut structs are prohibited from deriving these traits. This exclusion will be handled automatically by the macro.

#[extract(Unit, Single, Unnamed)]
#[enpow(UnwrapVar, VarAsRef)]
#[inner(derive(Debug, PartialEq))]
enum IpAddress {
    None,
    V4(u8, u8, u8, u8),
    V6(String),
    #[inner(derive(Clone))]
    Multi {
        v4: (u8, u8, u8, u8),
        v6: String,
    },
}

// Using PartialEq, Debug, and Clone derive
assert_eq!(
    IpAddress::Multi { v4: (0, 0, 0, 0), v6: "::".into() }.unwrap_multi(),
    IpAddressMulti { v4: (0, 0, 0, 0), v6: "::".into() }.clone()
);

// Using automatic Copy derive on Ref struct
let ip = IpAddress::Multi { v4: (0, 0, 0, 0), v6: "::".into() };
let copy = ip.unwrap_multi_as_ref();
let another_copy = copy;
assert_eq!(copy, IpAddressMultiRef { v4: &(0, 0, 0, 0), v6: &"::".into() });
assert_eq!(another_copy, IpAddressMultiRef { v4: &(0, 0, 0, 0), v6: &"::".into() });
Type Names

With the argument type_name or type_names, the standard way of naming the generated structs can be changed. There are multiple naming schemes possible, depending on the keyword or string literal provided:

  • type_name=EnumVar [default] will give each type the name of the enum followed by the name of the corresponding variant, e.g. IpAddressMulti
  • type_name=Var will let each type be named just like its corresponding variant, e.g. Multi
  • type_name=VarEnum will give each type the name of the corresponding variant followed by the name of the enum, e.g. MultiIpAddress
  • type_name="My{enum}Var{var}" will give each type the specified name with occurences of {enum} and {var} replaced by the name of the enum and the name of the corresponding variant, e.g. MyIpAddressMulti

This example generates the types IpNone, IpV4, IpV6, and IpV4_6.


#[extract(All)]
#[enpow(UnwrapVar)]
#[inner(type_names="Ip{var}", derive(Debug, PartialEq))]
#[derive(Debug, PartialEq)]
enum IpAddress {
    None,
    V4(u8, u8, u8, u8),
    V6(String),
    #[inner(type_name="IpV4_6")]
    Multi {
        v4: (u8, u8, u8, u8),
        v6: String,
    },
}

assert_eq!(IpAddress::from(IpNone).unwrap_none(), IpNone);
assert_eq!(IpAddress::from(IpV4(192, 168, 0, 1)).unwrap_v4(), IpV4(192, 168, 0, 1));
assert_eq!(IpAddress::from(IpV6("::1".into())).unwrap_v6(), IpV6("::1".into()));
assert_eq!(
    IpAddress::from(IpV4_6 { v4: (0, 0, 0, 0), v6: "::1".into() }).unwrap_multi(),
    IpV4_6 { v4: (0, 0, 0, 0), v6: "::1".into() }
);
Method Names

With the argument method_name, the standard way of naming the generated structs can be changed. The usage is method_name="my_new_name". The given name will be used instead of the variant’s name turned into snake_case. Note, that this argument can only be applied to variants directly, not to the enum itself.


#[extract(All)]
#[enpow(UnwrapVar)]
#[inner(type_names="Ip{var}", derive(Debug, PartialEq))]
#[derive(Debug, PartialEq)]
enum IpAddress {
    None,
    #[inner(method_name="ipv4")]
    V4(u8, u8, u8, u8),
    #[inner(method_name="ipv6")]
    V6(String),
    Multi {
        v4: (u8, u8, u8, u8),
        v6: String,
    },
}

assert_eq!(IpAddress::from(IpV4(192, 168, 0, 1)).unwrap_ipv4(), IpV4(192, 168, 0, 1));
assert_eq!(IpAddress::from(IpV6("::1".into())).unwrap_ipv6(), IpV6("::1".into()));