diff options
author | HampusM <hampus@hampusmat.com> | 2024-10-16 23:44:30 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2024-10-17 00:11:12 +0200 |
commit | 8ecd728f14da6ce1376822557fd63d3002a380de (patch) | |
tree | 872111591aa78538e55d20b8f41d0dd7e0cf4a1c /util-macros/src | |
parent | b7f0c2f965a7d460022d157ff149bf1ea498b9b4 (diff) |
feat(util-macros): add macro FromRepr
Diffstat (limited to 'util-macros/src')
-rw-r--r-- | util-macros/src/lib.rs | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/util-macros/src/lib.rs b/util-macros/src/lib.rs index 26f1936..83b5f58 100644 --- a/util-macros/src/lib.rs +++ b/util-macros/src/lib.rs @@ -1,5 +1,6 @@ use proc_macro::{TokenStream, TokenTree}; use quote::quote; +use syn::{parse, Ident, ItemEnum}; /// Subtracts two numbers and calls a given callback macro with the result. Optionally, a /// additional argument (delimeted) can be given which will also be passed to the @@ -139,3 +140,62 @@ pub fn sub(input: TokenStream) -> TokenStream } .into() } + +#[proc_macro_derive(FromRepr)] +pub fn from_repr(input: TokenStream) -> TokenStream +{ + let enum_item = parse::<ItemEnum>(input).unwrap(); + + let repr_attr = enum_item + .attrs + .iter() + .find(|attr| { + attr.path() + .get_ident() + .is_some_and(|attr_ident| attr_ident == "repr") + }) + .unwrap(); + + let repr = repr_attr.parse_args::<Ident>().unwrap(); + + let repr_str = repr.to_string(); + + if !((repr_str.starts_with('u') || repr_str.starts_with('i')) + && repr_str + .chars() + .skip(1) + .all(|character| character.is_ascii_digit())) + { + panic!("Invalid repr. Must be u* or i* where * is a number"); + } + + let variants = enum_item.variants.iter().map(|variant| { + let Some((_, discriminant)) = &variant.discriminant else { + panic!("All variants must have discriminants"); + }; + + (variant.ident.clone(), discriminant.clone()) + }); + + let match_arms = variants.map(|(variant, discriminant)| { + quote! { + #discriminant => Some(Self::#variant), + } + }); + + let enum_ident = enum_item.ident.clone(); + + quote! { + impl #enum_ident + { + pub fn from_repr(repr: #repr) -> Option<Self> + { + match repr { + #(#match_arms)* + _ => None + } + } + } + } + .into() +} |