1*4f2df630SAndroid Build Coastguard Worker // Copyright 2023 The ChromiumOS Authors
2*4f2df630SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*4f2df630SAndroid Build Coastguard Worker // found in the LICENSE file.
4*4f2df630SAndroid Build Coastguard Worker use std::cmp::Ordering;
5*4f2df630SAndroid Build Coastguard Worker
6*4f2df630SAndroid Build Coastguard Worker use proc_macro::*;
7*4f2df630SAndroid Build Coastguard Worker
get_enum_and_stream(stream: TokenStream) -> Option<(TokenTree, TokenStream)>8*4f2df630SAndroid Build Coastguard Worker fn get_enum_and_stream(stream: TokenStream) -> Option<(TokenTree, TokenStream)> {
9*4f2df630SAndroid Build Coastguard Worker let mut enum_name = None;
10*4f2df630SAndroid Build Coastguard Worker let mut cap_next = false;
11*4f2df630SAndroid Build Coastguard Worker for tree in stream {
12*4f2df630SAndroid Build Coastguard Worker match tree {
13*4f2df630SAndroid Build Coastguard Worker TokenTree::Ident(i) if i.to_string() == "enum" => cap_next = true,
14*4f2df630SAndroid Build Coastguard Worker TokenTree::Ident(i) if cap_next => {
15*4f2df630SAndroid Build Coastguard Worker enum_name = Some(TokenTree::Ident(i));
16*4f2df630SAndroid Build Coastguard Worker cap_next = false;
17*4f2df630SAndroid Build Coastguard Worker }
18*4f2df630SAndroid Build Coastguard Worker TokenTree::Group(g) => {
19*4f2df630SAndroid Build Coastguard Worker if let Some(name) = enum_name {
20*4f2df630SAndroid Build Coastguard Worker return Some((name, g.stream()));
21*4f2df630SAndroid Build Coastguard Worker } else {
22*4f2df630SAndroid Build Coastguard Worker let result = get_enum_and_stream(g.stream());
23*4f2df630SAndroid Build Coastguard Worker if result.is_some() {
24*4f2df630SAndroid Build Coastguard Worker return result;
25*4f2df630SAndroid Build Coastguard Worker }
26*4f2df630SAndroid Build Coastguard Worker };
27*4f2df630SAndroid Build Coastguard Worker }
28*4f2df630SAndroid Build Coastguard Worker _ => {}
29*4f2df630SAndroid Build Coastguard Worker }
30*4f2df630SAndroid Build Coastguard Worker }
31*4f2df630SAndroid Build Coastguard Worker None
32*4f2df630SAndroid Build Coastguard Worker }
33*4f2df630SAndroid Build Coastguard Worker
generate_enum_array(name: TokenStream, mut item: TokenStream, header: &str) -> TokenStream34*4f2df630SAndroid Build Coastguard Worker fn generate_enum_array(name: TokenStream, mut item: TokenStream, header: &str) -> TokenStream {
35*4f2df630SAndroid Build Coastguard Worker let (enum_name, enum_stream) = get_enum_and_stream(item.clone()).expect("Must use on enum");
36*4f2df630SAndroid Build Coastguard Worker // Check that enums do not have associated fields, but see still want to all doc comments.
37*4f2df630SAndroid Build Coastguard Worker for tree in enum_stream.clone() {
38*4f2df630SAndroid Build Coastguard Worker match tree {
39*4f2df630SAndroid Build Coastguard Worker TokenTree::Group(group) => match group.stream().into_iter().next() {
40*4f2df630SAndroid Build Coastguard Worker Some(TokenTree::Ident(ident)) if ident.to_string() == "doc" => {
41*4f2df630SAndroid Build Coastguard Worker // This is a doc comment; this is the only allowed group right now since we
42*4f2df630SAndroid Build Coastguard Worker // do not support associated fields on any enum value
43*4f2df630SAndroid Build Coastguard Worker }
44*4f2df630SAndroid Build Coastguard Worker _ => panic!("Only enums without associated fields are supported"),
45*4f2df630SAndroid Build Coastguard Worker },
46*4f2df630SAndroid Build Coastguard Worker _ => {
47*4f2df630SAndroid Build Coastguard Worker // If no groups, then this enum should just be a normal "C-Style" enum.
48*4f2df630SAndroid Build Coastguard Worker }
49*4f2df630SAndroid Build Coastguard Worker }
50*4f2df630SAndroid Build Coastguard Worker }
51*4f2df630SAndroid Build Coastguard Worker
52*4f2df630SAndroid Build Coastguard Worker let qualified_list = enum_stream
53*4f2df630SAndroid Build Coastguard Worker .into_iter()
54*4f2df630SAndroid Build Coastguard Worker .map(|tree| {
55*4f2df630SAndroid Build Coastguard Worker if let TokenTree::Ident(det_name) = tree {
56*4f2df630SAndroid Build Coastguard Worker format!("{}::{},", enum_name, det_name).parse().unwrap()
57*4f2df630SAndroid Build Coastguard Worker } else {
58*4f2df630SAndroid Build Coastguard Worker TokenStream::new()
59*4f2df630SAndroid Build Coastguard Worker }
60*4f2df630SAndroid Build Coastguard Worker })
61*4f2df630SAndroid Build Coastguard Worker .collect::<TokenStream>();
62*4f2df630SAndroid Build Coastguard Worker let array_stream: TokenStream = format!(
63*4f2df630SAndroid Build Coastguard Worker r#"{header}
64*4f2df630SAndroid Build Coastguard Worker pub const {name}: &[{enum_name}] = &[{qualified_list}];"#,
65*4f2df630SAndroid Build Coastguard Worker header = header,
66*4f2df630SAndroid Build Coastguard Worker name = name,
67*4f2df630SAndroid Build Coastguard Worker enum_name = enum_name,
68*4f2df630SAndroid Build Coastguard Worker qualified_list = qualified_list,
69*4f2df630SAndroid Build Coastguard Worker )
70*4f2df630SAndroid Build Coastguard Worker .parse()
71*4f2df630SAndroid Build Coastguard Worker .unwrap();
72*4f2df630SAndroid Build Coastguard Worker
73*4f2df630SAndroid Build Coastguard Worker // Emitted exactly what was written, then add the test array
74*4f2df630SAndroid Build Coastguard Worker item.extend(array_stream);
75*4f2df630SAndroid Build Coastguard Worker item
76*4f2df630SAndroid Build Coastguard Worker }
77*4f2df630SAndroid Build Coastguard Worker
78*4f2df630SAndroid Build Coastguard Worker /// Generates an test cfg array with the specified name containing all enum values. This is only
79*4f2df630SAndroid Build Coastguard Worker /// valid for enums where all the variants do not have fields associated with them.
80*4f2df630SAndroid Build Coastguard Worker /// Access to array is possible only in test builds
81*4f2df630SAndroid Build Coastguard Worker ///
82*4f2df630SAndroid Build Coastguard Worker /// # Example
83*4f2df630SAndroid Build Coastguard Worker ///
84*4f2df630SAndroid Build Coastguard Worker /// ```
85*4f2df630SAndroid Build Coastguard Worker /// # #[macro_use] extern crate enum_utils;
86*4f2df630SAndroid Build Coastguard Worker /// #[gen_test_enum_array(MyTestArrayName)]
87*4f2df630SAndroid Build Coastguard Worker /// enum MyEnum {
88*4f2df630SAndroid Build Coastguard Worker /// ValueOne,
89*4f2df630SAndroid Build Coastguard Worker /// ValueTwo,
90*4f2df630SAndroid Build Coastguard Worker /// }
91*4f2df630SAndroid Build Coastguard Worker ///
92*4f2df630SAndroid Build Coastguard Worker /// #[cfg(test)]
93*4f2df630SAndroid Build Coastguard Worker /// mod tests {
94*4f2df630SAndroid Build Coastguard Worker /// #[test]
95*4f2df630SAndroid Build Coastguard Worker /// fn test_two_values() {
96*4f2df630SAndroid Build Coastguard Worker /// assert_eq!(MyTestArrayName.len(), 2);
97*4f2df630SAndroid Build Coastguard Worker /// }
98*4f2df630SAndroid Build Coastguard Worker /// }
99*4f2df630SAndroid Build Coastguard Worker /// ```
100*4f2df630SAndroid Build Coastguard Worker #[proc_macro_attribute]
gen_test_enum_array(name: TokenStream, item: TokenStream) -> TokenStream101*4f2df630SAndroid Build Coastguard Worker pub fn gen_test_enum_array(name: TokenStream, item: TokenStream) -> TokenStream {
102*4f2df630SAndroid Build Coastguard Worker generate_enum_array(name, item, "#[cfg(test)]")
103*4f2df630SAndroid Build Coastguard Worker }
104*4f2df630SAndroid Build Coastguard Worker
105*4f2df630SAndroid Build Coastguard Worker /// Generates an array with the specified name containing all enum values. This is only valid for
106*4f2df630SAndroid Build Coastguard Worker /// enums where all the variants do not have fields associated with them.
107*4f2df630SAndroid Build Coastguard Worker ///
108*4f2df630SAndroid Build Coastguard Worker /// # Example
109*4f2df630SAndroid Build Coastguard Worker ///
110*4f2df630SAndroid Build Coastguard Worker /// ```
111*4f2df630SAndroid Build Coastguard Worker /// # #[macro_use] extern crate enum_utils;
112*4f2df630SAndroid Build Coastguard Worker /// #[gen_enum_array(MyArrayName)]
113*4f2df630SAndroid Build Coastguard Worker /// pub enum MyEnum {
114*4f2df630SAndroid Build Coastguard Worker /// ValueOne,
115*4f2df630SAndroid Build Coastguard Worker /// ValueTwo,
116*4f2df630SAndroid Build Coastguard Worker /// }
117*4f2df630SAndroid Build Coastguard Worker ///
118*4f2df630SAndroid Build Coastguard Worker /// fn check() {
119*4f2df630SAndroid Build Coastguard Worker /// assert_eq!(MyArrayName.len(), 2);
120*4f2df630SAndroid Build Coastguard Worker /// }
121*4f2df630SAndroid Build Coastguard Worker /// ```
122*4f2df630SAndroid Build Coastguard Worker #[proc_macro_attribute]
gen_enum_array(name: TokenStream, item: TokenStream) -> TokenStream123*4f2df630SAndroid Build Coastguard Worker pub fn gen_enum_array(name: TokenStream, item: TokenStream) -> TokenStream {
124*4f2df630SAndroid Build Coastguard Worker generate_enum_array(name, item, "")
125*4f2df630SAndroid Build Coastguard Worker }
126*4f2df630SAndroid Build Coastguard Worker
127*4f2df630SAndroid Build Coastguard Worker /// Generates an impl to_string for enum which returns &str.
128*4f2df630SAndroid Build Coastguard Worker /// Method is implemended using match for every enum variant.
129*4f2df630SAndroid Build Coastguard Worker /// Valid for enums where all the variants do not have fields associated with them.
130*4f2df630SAndroid Build Coastguard Worker ///
131*4f2df630SAndroid Build Coastguard Worker /// # Example
132*4f2df630SAndroid Build Coastguard Worker ///
133*4f2df630SAndroid Build Coastguard Worker /// ```
134*4f2df630SAndroid Build Coastguard Worker /// # #[macro_use] extern crate enum_utils;
135*4f2df630SAndroid Build Coastguard Worker /// #[gen_to_string]
136*4f2df630SAndroid Build Coastguard Worker /// enum MyEnum {
137*4f2df630SAndroid Build Coastguard Worker /// ValueOne,
138*4f2df630SAndroid Build Coastguard Worker /// ValueTwo,
139*4f2df630SAndroid Build Coastguard Worker /// }
140*4f2df630SAndroid Build Coastguard Worker ///
141*4f2df630SAndroid Build Coastguard Worker /// fn main() {
142*4f2df630SAndroid Build Coastguard Worker /// let e = MyEnum::ValueOne;
143*4f2df630SAndroid Build Coastguard Worker /// println!("{}", e.to_string());
144*4f2df630SAndroid Build Coastguard Worker /// }
145*4f2df630SAndroid Build Coastguard Worker ///
146*4f2df630SAndroid Build Coastguard Worker /// ```
147*4f2df630SAndroid Build Coastguard Worker #[proc_macro_attribute]
gen_to_string(_attr: TokenStream, mut input: TokenStream) -> TokenStream148*4f2df630SAndroid Build Coastguard Worker pub fn gen_to_string(_attr: TokenStream, mut input: TokenStream) -> TokenStream {
149*4f2df630SAndroid Build Coastguard Worker let (enum_name, enum_stream) = get_enum_and_stream(input.clone()).expect("Must use on enum");
150*4f2df630SAndroid Build Coastguard Worker
151*4f2df630SAndroid Build Coastguard Worker let mut match_arms = TokenStream::new();
152*4f2df630SAndroid Build Coastguard Worker let enums_items = enum_stream.into_iter().filter_map(|tt| match tt {
153*4f2df630SAndroid Build Coastguard Worker TokenTree::Ident(id) => Some(id.to_string()),
154*4f2df630SAndroid Build Coastguard Worker _ => None,
155*4f2df630SAndroid Build Coastguard Worker });
156*4f2df630SAndroid Build Coastguard Worker for item in enums_items {
157*4f2df630SAndroid Build Coastguard Worker let arm: TokenStream = format!(
158*4f2df630SAndroid Build Coastguard Worker r#"{enum_name}::{item} => "{item}","#,
159*4f2df630SAndroid Build Coastguard Worker enum_name = enum_name,
160*4f2df630SAndroid Build Coastguard Worker item = item,
161*4f2df630SAndroid Build Coastguard Worker )
162*4f2df630SAndroid Build Coastguard Worker .parse()
163*4f2df630SAndroid Build Coastguard Worker .unwrap();
164*4f2df630SAndroid Build Coastguard Worker match_arms.extend(arm);
165*4f2df630SAndroid Build Coastguard Worker }
166*4f2df630SAndroid Build Coastguard Worker
167*4f2df630SAndroid Build Coastguard Worker let implementation: TokenStream = format!(
168*4f2df630SAndroid Build Coastguard Worker r#"impl {enum_name} {{
169*4f2df630SAndroid Build Coastguard Worker pub fn to_string(&self) -> &'static str {{
170*4f2df630SAndroid Build Coastguard Worker match *self {{
171*4f2df630SAndroid Build Coastguard Worker {match_arms}
172*4f2df630SAndroid Build Coastguard Worker }}
173*4f2df630SAndroid Build Coastguard Worker }}
174*4f2df630SAndroid Build Coastguard Worker }}"#,
175*4f2df630SAndroid Build Coastguard Worker enum_name = enum_name,
176*4f2df630SAndroid Build Coastguard Worker match_arms = match_arms
177*4f2df630SAndroid Build Coastguard Worker )
178*4f2df630SAndroid Build Coastguard Worker .parse()
179*4f2df630SAndroid Build Coastguard Worker .unwrap();
180*4f2df630SAndroid Build Coastguard Worker
181*4f2df630SAndroid Build Coastguard Worker // Emit input as is and add the to_string implementation
182*4f2df630SAndroid Build Coastguard Worker input.extend(implementation);
183*4f2df630SAndroid Build Coastguard Worker input
184*4f2df630SAndroid Build Coastguard Worker }
185*4f2df630SAndroid Build Coastguard Worker
parse_to_i128(val: &str, negative: bool) -> i128186*4f2df630SAndroid Build Coastguard Worker fn parse_to_i128(val: &str, negative: bool) -> i128 {
187*4f2df630SAndroid Build Coastguard Worker let (first_pos, base) = match val.get(0..2) {
188*4f2df630SAndroid Build Coastguard Worker Some("0x") => (2, 16),
189*4f2df630SAndroid Build Coastguard Worker Some("0o") => (2, 8),
190*4f2df630SAndroid Build Coastguard Worker Some("0b") => (2, 2),
191*4f2df630SAndroid Build Coastguard Worker _ => (0, 10),
192*4f2df630SAndroid Build Coastguard Worker };
193*4f2df630SAndroid Build Coastguard Worker
194*4f2df630SAndroid Build Coastguard Worker let sign = if negative { -1 } else { 1 };
195*4f2df630SAndroid Build Coastguard Worker
196*4f2df630SAndroid Build Coastguard Worker // Remove any helper _ in the string literal, then convert from base
197*4f2df630SAndroid Build Coastguard Worker sign * i128::from_str_radix(
198*4f2df630SAndroid Build Coastguard Worker &val[first_pos..]
199*4f2df630SAndroid Build Coastguard Worker .chars()
200*4f2df630SAndroid Build Coastguard Worker .filter(|c| *c != '_')
201*4f2df630SAndroid Build Coastguard Worker .collect::<String>(),
202*4f2df630SAndroid Build Coastguard Worker base,
203*4f2df630SAndroid Build Coastguard Worker )
204*4f2df630SAndroid Build Coastguard Worker .unwrap_or_else(|_| panic!("Invalid number {}", val))
205*4f2df630SAndroid Build Coastguard Worker }
206*4f2df630SAndroid Build Coastguard Worker
207*4f2df630SAndroid Build Coastguard Worker /// Generates a exclusive `END` const and `from_<repr>` function for an enum
208*4f2df630SAndroid Build Coastguard Worker ///
209*4f2df630SAndroid Build Coastguard Worker /// # Example
210*4f2df630SAndroid Build Coastguard Worker ///
211*4f2df630SAndroid Build Coastguard Worker /// ```
212*4f2df630SAndroid Build Coastguard Worker /// # #[macro_use] extern crate enum_utils;
213*4f2df630SAndroid Build Coastguard Worker /// #[enum_as(u8)]
214*4f2df630SAndroid Build Coastguard Worker /// enum MyEnum {
215*4f2df630SAndroid Build Coastguard Worker /// ValueZero,
216*4f2df630SAndroid Build Coastguard Worker /// ValueOne,
217*4f2df630SAndroid Build Coastguard Worker /// }
218*4f2df630SAndroid Build Coastguard Worker ///
219*4f2df630SAndroid Build Coastguard Worker /// # fn main() {
220*4f2df630SAndroid Build Coastguard Worker /// assert!(matches!(MyEnum::from_u8(1), Some(MyEnum::ValueOne)));
221*4f2df630SAndroid Build Coastguard Worker /// assert_eq!(MyEnum::END, 2);
222*4f2df630SAndroid Build Coastguard Worker /// # }
223*4f2df630SAndroid Build Coastguard Worker ///
224*4f2df630SAndroid Build Coastguard Worker /// ```
225*4f2df630SAndroid Build Coastguard Worker #[proc_macro_attribute]
enum_as(repr: TokenStream, input: TokenStream) -> TokenStream226*4f2df630SAndroid Build Coastguard Worker pub fn enum_as(repr: TokenStream, input: TokenStream) -> TokenStream {
227*4f2df630SAndroid Build Coastguard Worker let repr = repr.to_string();
228*4f2df630SAndroid Build Coastguard Worker let (enum_name, enum_stream) = get_enum_and_stream(input.clone()).expect("Must use on enum");
229*4f2df630SAndroid Build Coastguard Worker
230*4f2df630SAndroid Build Coastguard Worker #[derive(Debug, Copy, Clone)]
231*4f2df630SAndroid Build Coastguard Worker enum WantState {
232*4f2df630SAndroid Build Coastguard Worker Determinant,
233*4f2df630SAndroid Build Coastguard Worker Punc,
234*4f2df630SAndroid Build Coastguard Worker NegativeVal,
235*4f2df630SAndroid Build Coastguard Worker Val,
236*4f2df630SAndroid Build Coastguard Worker CommentBlock,
237*4f2df630SAndroid Build Coastguard Worker }
238*4f2df630SAndroid Build Coastguard Worker let mut state = WantState::Determinant;
239*4f2df630SAndroid Build Coastguard Worker let mut skipped_ranges = vec![];
240*4f2df630SAndroid Build Coastguard Worker let mut start: Option<i128> = None;
241*4f2df630SAndroid Build Coastguard Worker let mut end: Option<i128> = None;
242*4f2df630SAndroid Build Coastguard Worker for tt in enum_stream {
243*4f2df630SAndroid Build Coastguard Worker use WantState::*;
244*4f2df630SAndroid Build Coastguard Worker state = match (tt, state) {
245*4f2df630SAndroid Build Coastguard Worker (TokenTree::Ident(_), Determinant) => Punc,
246*4f2df630SAndroid Build Coastguard Worker // If we are expecting a Determinant, but get a # instead, it must be a comment
247*4f2df630SAndroid Build Coastguard Worker (TokenTree::Punct(p), Determinant) if p.as_char() == '#' => CommentBlock,
248*4f2df630SAndroid Build Coastguard Worker (TokenTree::Punct(p), Val) if p.as_char() == '-' => NegativeVal,
249*4f2df630SAndroid Build Coastguard Worker (TokenTree::Group(_), CommentBlock) => Determinant,
250*4f2df630SAndroid Build Coastguard Worker (TokenTree::Punct(p), Punc) => match p.as_char() {
251*4f2df630SAndroid Build Coastguard Worker ',' => {
252*4f2df630SAndroid Build Coastguard Worker start = Some(start.unwrap_or(0));
253*4f2df630SAndroid Build Coastguard Worker end = Some(end.unwrap_or_else(|| start.unwrap()) + 1);
254*4f2df630SAndroid Build Coastguard Worker Determinant
255*4f2df630SAndroid Build Coastguard Worker }
256*4f2df630SAndroid Build Coastguard Worker '=' => Val,
257*4f2df630SAndroid Build Coastguard Worker other => panic!("Unexpected punctuation '{}'", other),
258*4f2df630SAndroid Build Coastguard Worker },
259*4f2df630SAndroid Build Coastguard Worker (TokenTree::Literal(l), Val | NegativeVal) => {
260*4f2df630SAndroid Build Coastguard Worker let val = parse_to_i128(&l.to_string(), matches!(state, NegativeVal));
261*4f2df630SAndroid Build Coastguard Worker start = Some(start.unwrap_or(val));
262*4f2df630SAndroid Build Coastguard Worker let expected = end.unwrap_or_else(|| start.unwrap());
263*4f2df630SAndroid Build Coastguard Worker match val.cmp(&expected) {
264*4f2df630SAndroid Build Coastguard Worker Ordering::Greater => {
265*4f2df630SAndroid Build Coastguard Worker skipped_ranges.push((expected, val));
266*4f2df630SAndroid Build Coastguard Worker end = Some(val);
267*4f2df630SAndroid Build Coastguard Worker }
268*4f2df630SAndroid Build Coastguard Worker Ordering::Less => panic!("Discriminants must increase in value"),
269*4f2df630SAndroid Build Coastguard Worker Ordering::Equal => (),
270*4f2df630SAndroid Build Coastguard Worker }
271*4f2df630SAndroid Build Coastguard Worker Punc
272*4f2df630SAndroid Build Coastguard Worker }
273*4f2df630SAndroid Build Coastguard Worker (tt, want) => {
274*4f2df630SAndroid Build Coastguard Worker panic!("Want {:?} but got {:?}", want, tt)
275*4f2df630SAndroid Build Coastguard Worker }
276*4f2df630SAndroid Build Coastguard Worker };
277*4f2df630SAndroid Build Coastguard Worker }
278*4f2df630SAndroid Build Coastguard Worker
279*4f2df630SAndroid Build Coastguard Worker let skipped_ranges = if skipped_ranges.is_empty() {
280*4f2df630SAndroid Build Coastguard Worker "".to_string()
281*4f2df630SAndroid Build Coastguard Worker } else {
282*4f2df630SAndroid Build Coastguard Worker format!(
283*4f2df630SAndroid Build Coastguard Worker r#"for r in &{ranges:?} {{
284*4f2df630SAndroid Build Coastguard Worker if (r.0..r.1).contains(&val) {{
285*4f2df630SAndroid Build Coastguard Worker return None;
286*4f2df630SAndroid Build Coastguard Worker }}
287*4f2df630SAndroid Build Coastguard Worker }}"#,
288*4f2df630SAndroid Build Coastguard Worker ranges = skipped_ranges
289*4f2df630SAndroid Build Coastguard Worker )
290*4f2df630SAndroid Build Coastguard Worker };
291*4f2df630SAndroid Build Coastguard Worker
292*4f2df630SAndroid Build Coastguard Worker // Ensure that there is at least one discriminant
293*4f2df630SAndroid Build Coastguard Worker let start = start.expect("Enum needs at least one discriminant");
294*4f2df630SAndroid Build Coastguard Worker let end = end.expect("Enum needs at least one discriminant");
295*4f2df630SAndroid Build Coastguard Worker
296*4f2df630SAndroid Build Coastguard Worker // Ensure that END will fit into usize or u64
297*4f2df630SAndroid Build Coastguard Worker u64::try_from(end).unwrap_or_else(|_| panic!("Value after last discriminant must be unsigned"));
298*4f2df630SAndroid Build Coastguard Worker
299*4f2df630SAndroid Build Coastguard Worker let implementation: TokenStream = format!(
300*4f2df630SAndroid Build Coastguard Worker r#"impl {enum_name} {{
301*4f2df630SAndroid Build Coastguard Worker pub const END: {end_type} = {end};
302*4f2df630SAndroid Build Coastguard Worker
303*4f2df630SAndroid Build Coastguard Worker pub fn from_{repr}(val: {repr}) -> Option<Self> {{
304*4f2df630SAndroid Build Coastguard Worker if val < {start} {{
305*4f2df630SAndroid Build Coastguard Worker return None;
306*4f2df630SAndroid Build Coastguard Worker }}
307*4f2df630SAndroid Build Coastguard Worker if val > ((Self::END - 1) as {repr}) {{
308*4f2df630SAndroid Build Coastguard Worker return None;
309*4f2df630SAndroid Build Coastguard Worker }}
310*4f2df630SAndroid Build Coastguard Worker {skipped_ranges}
311*4f2df630SAndroid Build Coastguard Worker Some( unsafe {{ core::mem::transmute(val) }})
312*4f2df630SAndroid Build Coastguard Worker }}
313*4f2df630SAndroid Build Coastguard Worker }}
314*4f2df630SAndroid Build Coastguard Worker
315*4f2df630SAndroid Build Coastguard Worker impl PartialEq for {enum_name} {{
316*4f2df630SAndroid Build Coastguard Worker fn eq(&self, other: &Self) -> bool {{
317*4f2df630SAndroid Build Coastguard Worker *self as {repr} == *other as {repr}
318*4f2df630SAndroid Build Coastguard Worker }}
319*4f2df630SAndroid Build Coastguard Worker }}
320*4f2df630SAndroid Build Coastguard Worker
321*4f2df630SAndroid Build Coastguard Worker impl Eq for {enum_name} {{ }}
322*4f2df630SAndroid Build Coastguard Worker
323*4f2df630SAndroid Build Coastguard Worker #[cfg(not(target_arch = "riscv32"))]
324*4f2df630SAndroid Build Coastguard Worker impl core::hash::Hash for {enum_name} {{
325*4f2df630SAndroid Build Coastguard Worker fn hash<H: core::hash::Hasher>(&self, state: &mut H) {{
326*4f2df630SAndroid Build Coastguard Worker (*self as {repr}).hash(state);
327*4f2df630SAndroid Build Coastguard Worker }}
328*4f2df630SAndroid Build Coastguard Worker }}"#,
329*4f2df630SAndroid Build Coastguard Worker enum_name = enum_name,
330*4f2df630SAndroid Build Coastguard Worker start = start,
331*4f2df630SAndroid Build Coastguard Worker end = end,
332*4f2df630SAndroid Build Coastguard Worker end_type = if repr == "u64" { "u64" } else { "usize " },
333*4f2df630SAndroid Build Coastguard Worker repr = repr,
334*4f2df630SAndroid Build Coastguard Worker skipped_ranges = skipped_ranges,
335*4f2df630SAndroid Build Coastguard Worker )
336*4f2df630SAndroid Build Coastguard Worker .parse()
337*4f2df630SAndroid Build Coastguard Worker .unwrap();
338*4f2df630SAndroid Build Coastguard Worker
339*4f2df630SAndroid Build Coastguard Worker // Attribute input with a repr, Clone, Clone, and allow(dead_code), then add the custom
340*4f2df630SAndroid Build Coastguard Worker // implementation. We allow dead code since the these enums are typically interface enums that
341*4f2df630SAndroid Build Coastguard Worker // define an API boundary.
342*4f2df630SAndroid Build Coastguard Worker let mut res: TokenStream = format!(
343*4f2df630SAndroid Build Coastguard Worker "#[repr({})]\n#[derive(Copy, Clone)]\n#[allow(dead_code)]",
344*4f2df630SAndroid Build Coastguard Worker repr
345*4f2df630SAndroid Build Coastguard Worker )
346*4f2df630SAndroid Build Coastguard Worker .parse()
347*4f2df630SAndroid Build Coastguard Worker .unwrap();
348*4f2df630SAndroid Build Coastguard Worker res.extend(input);
349*4f2df630SAndroid Build Coastguard Worker res.extend(implementation);
350*4f2df630SAndroid Build Coastguard Worker res
351*4f2df630SAndroid Build Coastguard Worker }
352*4f2df630SAndroid Build Coastguard Worker
get_param_list_without_self(params: TokenStream) -> String353*4f2df630SAndroid Build Coastguard Worker fn get_param_list_without_self(params: TokenStream) -> String {
354*4f2df630SAndroid Build Coastguard Worker let mut result = String::new();
355*4f2df630SAndroid Build Coastguard Worker
356*4f2df630SAndroid Build Coastguard Worker #[derive(Debug, Copy, Clone)]
357*4f2df630SAndroid Build Coastguard Worker enum WantState {
358*4f2df630SAndroid Build Coastguard Worker SelfParam,
359*4f2df630SAndroid Build Coastguard Worker FirstIdentifier,
360*4f2df630SAndroid Build Coastguard Worker Comma,
361*4f2df630SAndroid Build Coastguard Worker }
362*4f2df630SAndroid Build Coastguard Worker let mut state = WantState::SelfParam;
363*4f2df630SAndroid Build Coastguard Worker for tt in params {
364*4f2df630SAndroid Build Coastguard Worker use WantState::*;
365*4f2df630SAndroid Build Coastguard Worker state = match (tt, state) {
366*4f2df630SAndroid Build Coastguard Worker (TokenTree::Ident(ident), SelfParam) if ident.to_string() == "self" => FirstIdentifier,
367*4f2df630SAndroid Build Coastguard Worker (TokenTree::Ident(ident), FirstIdentifier) => {
368*4f2df630SAndroid Build Coastguard Worker result.push_str(&ident.to_string());
369*4f2df630SAndroid Build Coastguard Worker Comma
370*4f2df630SAndroid Build Coastguard Worker }
371*4f2df630SAndroid Build Coastguard Worker (TokenTree::Punct(p), Comma) if p.to_string() == "," => {
372*4f2df630SAndroid Build Coastguard Worker result.push(',');
373*4f2df630SAndroid Build Coastguard Worker FirstIdentifier
374*4f2df630SAndroid Build Coastguard Worker }
375*4f2df630SAndroid Build Coastguard Worker (_, other) => {
376*4f2df630SAndroid Build Coastguard Worker // Do nothing; keep watching for what we are looking for
377*4f2df630SAndroid Build Coastguard Worker other
378*4f2df630SAndroid Build Coastguard Worker }
379*4f2df630SAndroid Build Coastguard Worker };
380*4f2df630SAndroid Build Coastguard Worker }
381*4f2df630SAndroid Build Coastguard Worker
382*4f2df630SAndroid Build Coastguard Worker result
383*4f2df630SAndroid Build Coastguard Worker }
384*4f2df630SAndroid Build Coastguard Worker
385*4f2df630SAndroid Build Coastguard Worker /// Replaces the function body with a single statement that pass all parameters thru to same
386*4f2df630SAndroid Build Coastguard Worker /// function name on the variable specified in the passthru_to macro
387*4f2df630SAndroid Build Coastguard Worker ///
388*4f2df630SAndroid Build Coastguard Worker /// # Example
389*4f2df630SAndroid Build Coastguard Worker ///
390*4f2df630SAndroid Build Coastguard Worker /// ```
391*4f2df630SAndroid Build Coastguard Worker /// # #[macro_use] extern crate enum_utils;
392*4f2df630SAndroid Build Coastguard Worker /// pub struct Inner(usize);
393*4f2df630SAndroid Build Coastguard Worker ///
394*4f2df630SAndroid Build Coastguard Worker /// impl Inner {
395*4f2df630SAndroid Build Coastguard Worker /// pub fn read_plus(&self, plus: usize) -> usize {
396*4f2df630SAndroid Build Coastguard Worker /// self.0 + plus
397*4f2df630SAndroid Build Coastguard Worker /// }
398*4f2df630SAndroid Build Coastguard Worker /// }
399*4f2df630SAndroid Build Coastguard Worker ///
400*4f2df630SAndroid Build Coastguard Worker /// pub struct Outer {
401*4f2df630SAndroid Build Coastguard Worker /// pub my_inner: Inner,
402*4f2df630SAndroid Build Coastguard Worker /// }
403*4f2df630SAndroid Build Coastguard Worker ///
404*4f2df630SAndroid Build Coastguard Worker /// impl Outer {
405*4f2df630SAndroid Build Coastguard Worker /// #[passthru_to(my_inner)]
406*4f2df630SAndroid Build Coastguard Worker /// pub fn read_plus(&self, plus: usize) -> usize {}
407*4f2df630SAndroid Build Coastguard Worker /// }
408*4f2df630SAndroid Build Coastguard Worker ///
409*4f2df630SAndroid Build Coastguard Worker /// ```
410*4f2df630SAndroid Build Coastguard Worker #[proc_macro_attribute]
passthru_to(passthru_var: TokenStream, input: TokenStream) -> TokenStream411*4f2df630SAndroid Build Coastguard Worker pub fn passthru_to(passthru_var: TokenStream, input: TokenStream) -> TokenStream {
412*4f2df630SAndroid Build Coastguard Worker #[derive(Debug, Copy, Clone)]
413*4f2df630SAndroid Build Coastguard Worker enum WantState {
414*4f2df630SAndroid Build Coastguard Worker FunctionKeyword,
415*4f2df630SAndroid Build Coastguard Worker FunctionName,
416*4f2df630SAndroid Build Coastguard Worker Parameters,
417*4f2df630SAndroid Build Coastguard Worker Body,
418*4f2df630SAndroid Build Coastguard Worker End,
419*4f2df630SAndroid Build Coastguard Worker }
420*4f2df630SAndroid Build Coastguard Worker let mut state = WantState::FunctionKeyword;
421*4f2df630SAndroid Build Coastguard Worker let mut name = None;
422*4f2df630SAndroid Build Coastguard Worker let mut params = None;
423*4f2df630SAndroid Build Coastguard Worker let mut body_num = None;
424*4f2df630SAndroid Build Coastguard Worker for (i, tt) in input.clone().into_iter().enumerate() {
425*4f2df630SAndroid Build Coastguard Worker use WantState::*;
426*4f2df630SAndroid Build Coastguard Worker state = match (tt, state) {
427*4f2df630SAndroid Build Coastguard Worker (TokenTree::Ident(ident), FunctionKeyword) if ident.to_string() == "fn" => FunctionName,
428*4f2df630SAndroid Build Coastguard Worker (_, FunctionKeyword) => FunctionKeyword,
429*4f2df630SAndroid Build Coastguard Worker (TokenTree::Ident(ident), FunctionName) => {
430*4f2df630SAndroid Build Coastguard Worker name = Some(ident.to_string());
431*4f2df630SAndroid Build Coastguard Worker Parameters
432*4f2df630SAndroid Build Coastguard Worker }
433*4f2df630SAndroid Build Coastguard Worker (TokenTree::Group(group), Parameters) => {
434*4f2df630SAndroid Build Coastguard Worker params = Some(get_param_list_without_self(group.stream()));
435*4f2df630SAndroid Build Coastguard Worker Body
436*4f2df630SAndroid Build Coastguard Worker }
437*4f2df630SAndroid Build Coastguard Worker (TokenTree::Group(group), Body) if group.delimiter() == Delimiter::Brace => {
438*4f2df630SAndroid Build Coastguard Worker body_num = Some(i);
439*4f2df630SAndroid Build Coastguard Worker End
440*4f2df630SAndroid Build Coastguard Worker }
441*4f2df630SAndroid Build Coastguard Worker (_, Body) => Body,
442*4f2df630SAndroid Build Coastguard Worker (tt, want) => {
443*4f2df630SAndroid Build Coastguard Worker panic!("Want {:?} but got {:?}", want, tt)
444*4f2df630SAndroid Build Coastguard Worker }
445*4f2df630SAndroid Build Coastguard Worker };
446*4f2df630SAndroid Build Coastguard Worker }
447*4f2df630SAndroid Build Coastguard Worker
448*4f2df630SAndroid Build Coastguard Worker let implementation: TokenStream = format!(
449*4f2df630SAndroid Build Coastguard Worker r#"{{ self.{passthru_var}.{name}({params}) }}"#,
450*4f2df630SAndroid Build Coastguard Worker passthru_var = passthru_var,
451*4f2df630SAndroid Build Coastguard Worker name = name.expect("Cannot find function name"),
452*4f2df630SAndroid Build Coastguard Worker params = params.expect("Could find parameters"),
453*4f2df630SAndroid Build Coastguard Worker )
454*4f2df630SAndroid Build Coastguard Worker .parse()
455*4f2df630SAndroid Build Coastguard Worker .unwrap();
456*4f2df630SAndroid Build Coastguard Worker
457*4f2df630SAndroid Build Coastguard Worker // Take everything up to body, then replace body with the passthru implementation
458*4f2df630SAndroid Build Coastguard Worker input
459*4f2df630SAndroid Build Coastguard Worker .into_iter()
460*4f2df630SAndroid Build Coastguard Worker .take(body_num.expect("Cannot find body"))
461*4f2df630SAndroid Build Coastguard Worker .chain(implementation)
462*4f2df630SAndroid Build Coastguard Worker .collect()
463*4f2df630SAndroid Build Coastguard Worker }
464