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) -> Option<&'a Enum> {
55 protocol
56 .interfaces
57 .iter()
58 .find_map(|interface| interface.enums.iter().find(|e| e.name == name))
59}
60
61pub fn write_enums(interface: &Interface) -> Vec<TokenStream> {
62 let mut enums = Vec::new();
63
64 for e in &interface.enums {
65 let docs = description_to_docs(e.description.as_ref());
66 let name = make_ident(e.name.to_upper_camel_case());
67
68 if !e.bitfield {
69 let mut variants = Vec::new();
70 let mut match_variants = Vec::new();
71
72 for entry in &e.entries {
73 let docs = description_to_docs(entry.summary.as_ref());
74 let name = make_ident(entry.name.to_upper_camel_case());
75 let value = value_to_u32(&entry.value);
76
77 variants.push(quote! {
78 #(#docs)*
79 #name = #value
80 });
81
82 match_variants.push(quote! { #value => { Ok(Self::#name) } });
83 }
84
85 enums.push(quote! {
86 #(#docs)*
87 #[repr(u32)]
88 #[non_exhaustive]
89 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
90 pub enum #name {
91 #(#variants),*
92 }
93
94 impl TryFrom<u32> for #name {
95 type Error = crate::wire::DecodeError;
96
97 fn try_from(v: u32) -> Result<Self, Self::Error> {
98 match v {
99 #(#match_variants),*
100 _ => Err(crate::wire::DecodeError::MalformedPayload)
101 }
102 }
103 }
104
105 impl std::fmt::Display for #name {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 (*self as u32).fmt(f)
108 }
109 }
110 })
111 } else {
112 let mut variants = Vec::new();
113
114 for entry in &e.entries {
115 let name = make_ident(entry.name.to_upper_camel_case());
116
117 let docs = description_to_docs(entry.summary.as_ref());
118
119 let value = value_to_u32(&entry.value);
120
121 variants.push(quote! {
122 #(#docs)*
123 const #name = #value;
124 });
125 }
126
127 enums.push(quote! {
128 bitflags::bitflags! {
129 #(#docs)*
130 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
131 pub struct #name: u32 {
132 #(#variants)*
133 }
134 }
135
136 impl TryFrom<u32> for #name {
137 type Error = crate::wire::DecodeError;
138
139 fn try_from(v: u32) -> Result<Self, Self::Error> {
140 Self::from_bits(v).ok_or(crate::wire::DecodeError::MalformedPayload)
141 }
142 }
143
144 impl std::fmt::Display for #name {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 self.bits().fmt(f)
147 }
148 }
149 })
150 }
151 }
152
153 enums
154}