summaryrefslogtreecommitdiff
path: root/util-macros/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2024-10-16 23:44:30 +0200
committerHampusM <hampus@hampusmat.com>2024-10-17 00:11:12 +0200
commit8ecd728f14da6ce1376822557fd63d3002a380de (patch)
tree872111591aa78538e55d20b8f41d0dd7e0cf4a1c /util-macros/src
parentb7f0c2f965a7d460022d157ff149bf1ea498b9b4 (diff)
feat(util-macros): add macro FromRepr
Diffstat (limited to 'util-macros/src')
-rw-r--r--util-macros/src/lib.rs60
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()
+}