summaryrefslogtreecommitdiff
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
parentb7f0c2f965a7d460022d157ff149bf1ea498b9b4 (diff)
feat(util-macros): add macro FromRepr
-rw-r--r--Cargo.lock1
-rw-r--r--util-macros/Cargo.toml2
-rw-r--r--util-macros/src/lib.rs60
3 files changed, 63 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a203ee2..76cb864 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -666,6 +666,7 @@ version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
+ "syn",
]
[[package]]
diff --git a/util-macros/Cargo.toml b/util-macros/Cargo.toml
index b8a14e5..efd017d 100644
--- a/util-macros/Cargo.toml
+++ b/util-macros/Cargo.toml
@@ -9,3 +9,5 @@ proc-macro = true
[dependencies]
quote = "1.0.35"
proc-macro2 = "1.0.78"
+syn = { version = "2.0.51", features = ["full"] }
+
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()
+}