Skip to main content

pm2/
lib.rs

1mod mune;
2
3use proc_macro::{
4  Delimiter, Spacing, TokenStream,
5  TokenTree::{self, *},
6  token_stream::IntoIter,
7};
8
9/// Extend enums to provide additional functionality
10///
11/// # Added constants
12///
13/// ## <code>REPR: [Option]<&[str]></code>
14///
15/// The type declared in `#[repr]` if present, as a string
16///
17/// ## <code>NUM_VARIANTS: [usize]</code>
18///
19/// The number of variants in the enum
20///
21/// ## `FIRST_VARIANT: Self`
22///
23/// The first variant as defined in source order
24///
25/// ## `LAST_VARIANT: Self`
26///
27/// The last variant as defined in source order
28///
29/// ## `MAX_DISCRIMINANT: <repr>`
30///
31/// **Available only on numeric `#[repr]`s.**
32///
33/// The highest numerical discriminant value among all variants. The type is the same as the
34/// `#[repr]` of the enum
35///
36/// # Features
37///
38/// ## `#[mune(deref_discriminant)]`
39///
40/// **Available only on numeric `#[repr]`s.**
41///
42/// Implements [`Deref`][::core::ops::Deref] with a [`Target`][::core::ops::Deref::Target] of the
43/// `#[repr]` of the enum allowing you to simply do `*MyEnum::Variant` to get the value of the
44/// discriminant
45///
46/// ## `#[mune(bitflags = <name_format: string>)]`
47///
48/// **Available only on unsigned numeric `#[repr]`s apart from `usize`.**
49///
50/// Generates associated bitflag constants for every variant in the enum using a `1 << i` shift
51/// pattern. `<name_format>` must be a string literal containing a single `{}` placeholder to format
52/// the names of the generated constants. The type of each constant will be the same as the `#[repr]`
53/// of the enum
54///
55/// **Note:** The `{}` placeholder will be replaced with the name of each variant converted to
56/// `SCREAMING_SNAKE_CASE` as per the [rust style guide](https://doc.rust-lang.org/style-guide/advice.html#names).
57/// The conversion is done using [`heck::ToShoutySnakeCase`]
58///
59/// # Limitations
60///
61/// * Generic parameters and `where` clauses are currently unsupported
62/// * Only unit enums are supported for now, and without explicit discriminants
63/// * Only `#[repr]`s with the type are supported
64///
65/// # Examples
66///
67/// ```
68/// #[pm2::mune]
69/// #[derive(Debug, PartialEq)]
70/// enum MyEnum {
71///   A,
72///   B,
73///   C,
74///   D,
75/// }
76///
77/// assert_eq!(MyEnum::REPR, None);
78/// assert_eq!(MyEnum::NUM_VARIANTS, 4);
79/// assert_eq!(MyEnum::FIRST_VARIANT, MyEnum::A);
80/// assert_eq!(MyEnum::LAST_VARIANT, MyEnum::D);
81/// ```
82///
83/// ```
84/// #[pm2::mune]
85/// #[repr(u8)]
86/// #[derive(Debug, PartialEq)]
87/// enum MyEnum {
88///   A,
89///   B,
90///   C,
91///   D,
92/// }
93///
94/// assert_eq!(MyEnum::REPR, Some("u8"));
95/// ```
96///
97/// ```
98/// #[pm2::mune(deref_discriminant)]
99/// #[repr(u8)]
100/// #[derive(Debug, PartialEq)]
101/// enum MyEnum {
102///   A,
103///   B,
104///   C,
105///   D,
106/// }
107///
108/// let a: u8 = *MyEnum::A;
109/// assert_eq!(a, 0);
110///
111/// assert_eq!(*MyEnum::B, 1);
112/// assert_eq!(*MyEnum::C, 2);
113/// assert_eq!(*MyEnum::D, 3);
114/// ```
115///
116/// ```
117/// #[pm2::mune(bitflags = "FLAG_{}")]
118/// #[repr(u8)]
119/// enum ModifierKey {
120///   Control,
121///   Alt,
122///   LeftShift,
123///   RightShift,
124///   Meta,
125/// }
126///
127/// assert_eq!(ModifierKey::FLAG_CONTROL, 1);
128/// assert_eq!(ModifierKey::FLAG_ALT, 2);
129/// assert_eq!(ModifierKey::FLAG_LEFT_SHIFT, 4);
130/// assert_eq!(ModifierKey::FLAG_RIGHT_SHIFT, 8);
131/// assert_eq!(ModifierKey::FLAG_META, 16);
132/// ```
133#[proc_macro_attribute]
134pub fn mune(attr: TokenStream, item: TokenStream) -> TokenStream {
135  mune::run(attr, item)
136}
137
138fn seek_and_collect_ident(s: &mut IntoIter, tokens: &mut Vec<TokenTree>, ident: &str) -> bool {
139  for t in s {
140    if let Ident(i) = &t
141      && i.to_string() == ident
142    {
143      return true;
144    }
145
146    tokens.push(t);
147  }
148
149  false
150}
151
152fn seek_ident(s: &mut IntoIter, ident: &str) -> bool {
153  for t in s {
154    if let Ident(i) = t
155      && i.to_string() == ident
156    {
157      return true;
158    }
159  }
160
161  false
162}
163
164fn get_idents(tt: &[TokenTree]) -> Vec<String> {
165  tt.iter()
166    .filter_map(|t| {
167      if let Ident(i) = t {
168        Some(i.to_string())
169      } else {
170        None
171      }
172    })
173    .collect()
174}
175
176fn get_repr(tt: &[TokenTree]) -> Option<String> {
177  for (i, t) in tt.iter().enumerate() {
178    if let Punct(p) = t
179      && *p == '#'
180      && p.spacing() == Spacing::Alone
181      && i < tt.len() - 1
182      && let Group(g) = &tt[i + 1]
183      && g.delimiter() == Delimiter::Bracket
184      && let mut s = g.stream().into_iter()
185      && let Some(Ident(id)) = s.next()
186      && id.to_string() == "repr"
187      && let Some(Group(g)) = s.next()
188      && g.delimiter() == Delimiter::Parenthesis
189      && let mut s = g.stream().into_iter()
190      && let Some(Ident(id)) = s.next()
191    {
192      return Some(id.to_string());
193    }
194  }
195
196  None
197}