1use heck::ToUpperCamelCase;
2use proc_macro2::{Ident, TokenStream};
3use quote::{format_ident, quote};
4use std::fmt::Display;
5
6use crate::parser::{Enum, Interface, Protocol};
7
8const KEYWORDS: [&str; 52] = [
9 "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn", "for",
10 "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return",
11 "self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe", "use", "where",
12 "while", "async", "await", "dyn", "abstract", "become", "box", "do", "final", "macro",
13 "override", "priv", "typeof", "unsized", "virtual", "yield", "try", "gen",
14];
15
16pub fn description_to_docs(description: Option<&String>) -> Vec<TokenStream> {
17 let mut docs = Vec::new();
18
19 if let Some(description) = description {
20 for line in description.lines() {
21 let doc = line.trim();
23 docs.push(quote! {#[doc = #doc]})
24 }
25 }
26
27 docs
28}
29
30pub fn value_to_u32(value: &str) -> u32 {
31 if let Some(s) = value.strip_prefix("0x") {
32 u32::from_str_radix(s, 16).expect("Invalid enum value")
33 } else {
34 value.parse().expect("Invalid enum value")
35 }
36}
37
38pub fn make_ident<D: Display>(ident: D) -> Ident {
39 let mut prefix = "";
40
41 if ident.to_string().chars().next().unwrap().is_numeric() {
42 prefix = "_"
43 }
44
45 let mut raw: &str = "";
46
47 if KEYWORDS.contains(&ident.to_string().as_str()) {
48 raw = "r#"
49 }
50
51 format_ident!("{raw}{prefix}{ident}")
52}
53
54pub fn find_enum<'a>(protocol: &'a Protocol, name: &str) -> &'a Enum {
55 protocol
56 .interfaces
57 .iter()
58 .find_map(|interface| interface.enums.iter().find(|e| e.name == name))
59 .unwrap()
60}
61
62pub fn write_enums(interface: &Interface) -> Vec<TokenStream> {
63 let mut enums = Vec::new();
64
65 for e in &interface.enums {
66 let docs = description_to_docs(e.description.as_ref());
67 let name = make_ident(e.name.to_upper_camel_case());
68
69 if !e.bitfield {
70 let mut variants = Vec::new();
71 let mut match_variants = Vec::new();
72
73 for entry in &e.entries {
74 let docs = description_to_docs(entry.summary.as_ref());
75 let name = make_ident(entry.name.to_upper_camel_case());
76 let value = value_to_u32(&entry.value);
77
78 variants.push(quote! {
79 #(#docs)*
80 #name = #value
81 });
82
83 match_variants.push(quote! { #value => { Ok(Self::#name) } });
84 }
85
86 enums.push(quote! {
87 #(#docs)*
88 #[repr(u32)]
89 #[non_exhaustive]
90 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
91 pub enum #name {
92 #(#variants),*
93 }
94
95 impl TryFrom<u32> for #name {
96 type Error = crate::wire::DecodeError;
97
98 fn try_from(v: u32) -> Result<Self, Self::Error> {
99 match v {
100 #(#match_variants),*
101 _ => Err(crate::wire::DecodeError::MalformedPayload)
102 }
103 }
104 }
105
106 impl std::fmt::Display for #name {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 (*self as u32).fmt(f)
109 }
110 }
111 })
112 } else {
113 let mut variants = Vec::new();
114
115 for entry in &e.entries {
116 let name = make_ident(entry.name.to_upper_camel_case());
117
118 let docs = description_to_docs(entry.summary.as_ref());
119
120 let value = value_to_u32(&entry.value);
121
122 variants.push(quote! {
123 #(#docs)*
124 const #name = #value;
125 });
126 }
127
128 enums.push(quote! {
129 bitflags::bitflags! {
130 #(#docs)*
131 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
132 pub struct #name: u32 {
133 #(#variants)*
134 }
135 }
136
137 impl TryFrom<u32> for #name {
138 type Error = crate::wire::DecodeError;
139
140 fn try_from(v: u32) -> Result<Self, Self::Error> {
141 Self::from_bits(v).ok_or(crate::wire::DecodeError::MalformedPayload)
142 }
143 }
144
145 impl std::fmt::Display for #name {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 self.bits().fmt(f)
148 }
149 }
150 })
151 }
152 }
153
154 enums
155}