|
1 | 1 | use heck::ToSnakeCase; |
2 | 2 | use proc_macro2::{Span, TokenStream}; |
3 | 3 | use quote::quote; |
4 | | -use syn::{Data, Field, Fields, Ident, Lifetime, Lit, Meta, TypeParam, Variant}; |
| 4 | +use syn::{Attribute, Data, Field, Fields, Ident, Lifetime, Lit, Meta, TypeParam, Variant}; |
5 | 5 |
|
6 | 6 | use super::RustlerAttr; |
7 | 7 |
|
@@ -110,29 +110,49 @@ impl<'a> Context<'a> { |
110 | 110 | .iter() |
111 | 111 | .map(|field| { |
112 | 112 | let atom_fun = Self::field_to_atom_fun(field); |
113 | | - |
114 | | - let ident = field.ident.as_ref().unwrap(); |
115 | | - let ident_str = ident.to_string(); |
116 | | - let ident_str = Self::remove_raw(&ident_str); |
| 113 | + let atom_name = Self::field_atom_name(field); |
117 | 114 |
|
118 | 115 | quote! { |
119 | | - #atom_fun = #ident_str, |
| 116 | + #atom_fun = #atom_name, |
120 | 117 | } |
121 | 118 | }) |
122 | 119 | .collect() |
123 | 120 | }) |
124 | 121 | } |
125 | 122 |
|
126 | 123 | pub fn field_to_atom_fun(field: &Field) -> Ident { |
127 | | - let ident = field.ident.as_ref().unwrap(); |
128 | | - Self::ident_to_atom_fun(ident) |
| 124 | + Self::atom_fun(&Self::field_atom_name(field)) |
| 125 | + } |
| 126 | + |
| 127 | + pub fn field_atom_name(field: &Field) -> String { |
| 128 | + Self::rename_attr(&field.attrs).unwrap_or_else(|| { |
| 129 | + let ident = field.ident.as_ref().unwrap(); |
| 130 | + Self::ident_to_atom_name(ident) |
| 131 | + }) |
| 132 | + } |
| 133 | + |
| 134 | + pub fn variant_to_atom_fun(variant: &Variant) -> Ident { |
| 135 | + Self::atom_fun(&Self::variant_atom_name(variant)) |
129 | 136 | } |
130 | 137 |
|
131 | | - pub fn ident_to_atom_fun(ident: &Ident) -> Ident { |
132 | | - let ident_str = ident.to_string().to_snake_case(); |
133 | | - let ident_str = Self::remove_raw(&ident_str); |
| 138 | + pub fn variant_atom_name(variant: &Variant) -> String { |
| 139 | + Self::rename_attr(&variant.attrs) |
| 140 | + .unwrap_or_else(|| Self::ident_to_atom_name(&variant.ident)) |
| 141 | + } |
| 142 | + |
| 143 | + pub fn ident_to_atom_name(ident: &Ident) -> String { |
| 144 | + let ident_str = ident.to_string(); |
| 145 | + Self::remove_raw(&ident_str).to_snake_case() |
| 146 | + } |
| 147 | + |
| 148 | + fn atom_fun(atom_name: &str) -> Ident { |
| 149 | + let suffix = atom_name |
| 150 | + .as_bytes() |
| 151 | + .iter() |
| 152 | + .map(|byte| format!("{byte:02x}")) |
| 153 | + .collect::<String>(); |
134 | 154 |
|
135 | | - Ident::new(&format!("atom_{ident_str}"), Span::call_site()) |
| 155 | + Ident::new(&format!("atom_{}", suffix), Span::call_site()) |
136 | 156 | } |
137 | 157 |
|
138 | 158 | pub fn escape_ident_with_index(ident_str: &str, index: usize, infix: &str) -> Ident { |
@@ -161,6 +181,33 @@ impl<'a> Context<'a> { |
161 | 181 | .expect("split has always at least one element") |
162 | 182 | } |
163 | 183 |
|
| 184 | + fn rename_attr(attrs: &[Attribute]) -> Option<String> { |
| 185 | + attrs.iter().find_map(|attr| { |
| 186 | + if !attr.path().is_ident("rustler") { |
| 187 | + return None; |
| 188 | + } |
| 189 | + |
| 190 | + let Meta::List(list) = &attr.meta else { |
| 191 | + return None; |
| 192 | + }; |
| 193 | + |
| 194 | + let mut rename = None; |
| 195 | + list.parse_nested_meta(|nested_meta| { |
| 196 | + if nested_meta.path.is_ident("rename") { |
| 197 | + let value = nested_meta.value()?; |
| 198 | + let lit: syn::LitStr = value.parse()?; |
| 199 | + rename = Some(lit.value()); |
| 200 | + Ok(()) |
| 201 | + } else { |
| 202 | + Err(nested_meta.error("Expected rename in rustler attribute")) |
| 203 | + } |
| 204 | + }) |
| 205 | + .unwrap_or_else(|err| panic!("{}", err)); |
| 206 | + |
| 207 | + rename |
| 208 | + }) |
| 209 | + } |
| 210 | + |
164 | 211 | fn encode_decode_attr_set(attrs: &[RustlerAttr]) -> bool { |
165 | 212 | attrs |
166 | 213 | .iter() |
|
0 commit comments