1extern crate proc_macro;
6
7use {
8 proc_macro::TokenStream,
9 proc_macro2::{Delimiter, Span, TokenTree},
10 quote::{quote, ToTokens},
11 std::convert::TryFrom,
12 syn::{
13 bracketed,
14 parse::{Parse, ParseStream, Result},
15 parse_macro_input,
16 punctuated::Punctuated,
17 token::Bracket,
18 Expr, Ident, LitByte, LitStr, Path, Token,
19 },
20};
21
22fn parse_id(
23 input: ParseStream,
24 pubkey_type: proc_macro2::TokenStream,
25) -> Result<proc_macro2::TokenStream> {
26 let id = if input.peek(syn::LitStr) {
27 let id_literal: LitStr = input.parse()?;
28 parse_pubkey(&id_literal, &pubkey_type)?
29 } else {
30 let expr: Expr = input.parse()?;
31 quote! { #expr }
32 };
33
34 if !input.is_empty() {
35 let stream: proc_macro2::TokenStream = input.parse()?;
36 return Err(syn::Error::new_spanned(stream, "unexpected token"));
37 }
38 Ok(id)
39}
40
41fn id_to_tokens(
42 id: &proc_macro2::TokenStream,
43 pubkey_type: proc_macro2::TokenStream,
44 tokens: &mut proc_macro2::TokenStream,
45) {
46 tokens.extend(quote! {
47 pub static ID: #pubkey_type = #id;
49
50 pub fn check_id(id: &#pubkey_type) -> bool {
52 id == &ID
53 }
54
55 pub fn id() -> #pubkey_type {
57 ID
58 }
59
60 #[cfg(test)]
61 #[test]
62 fn test_id() {
63 assert!(check_id(&id()));
64 }
65 });
66}
67
68fn deprecated_id_to_tokens(
69 id: &proc_macro2::TokenStream,
70 pubkey_type: proc_macro2::TokenStream,
71 tokens: &mut proc_macro2::TokenStream,
72) {
73 tokens.extend(quote! {
74 pub static ID: #pubkey_type = #id;
76
77 #[deprecated()]
79 pub fn check_id(id: &#pubkey_type) -> bool {
80 id == &ID
81 }
82
83 #[deprecated()]
85 pub fn id() -> #pubkey_type {
86 ID
87 }
88
89 #[cfg(test)]
90 #[test]
91 fn test_id() {
92 #[allow(deprecated)]
93 assert!(check_id(&id()));
94 }
95 });
96}
97
98struct SdkPubkey(proc_macro2::TokenStream);
99
100impl Parse for SdkPubkey {
101 fn parse(input: ParseStream) -> Result<Self> {
102 parse_id(input, quote! { ::solana_sdk::pubkey::Pubkey }).map(Self)
103 }
104}
105
106impl ToTokens for SdkPubkey {
107 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
108 let id = &self.0;
109 tokens.extend(quote! {#id})
110 }
111}
112
113struct ProgramSdkPubkey(proc_macro2::TokenStream);
114
115impl Parse for ProgramSdkPubkey {
116 fn parse(input: ParseStream) -> Result<Self> {
117 parse_id(input, quote! { ::solana_program::pubkey::Pubkey }).map(Self)
118 }
119}
120
121impl ToTokens for ProgramSdkPubkey {
122 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
123 let id = &self.0;
124 tokens.extend(quote! {#id})
125 }
126}
127
128struct Id(proc_macro2::TokenStream);
129
130impl Parse for Id {
131 fn parse(input: ParseStream) -> Result<Self> {
132 parse_id(input, quote! { ::solana_sdk::pubkey::Pubkey }).map(Self)
133 }
134}
135
136impl ToTokens for Id {
137 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
138 id_to_tokens(&self.0, quote! { ::solana_sdk::pubkey::Pubkey }, tokens)
139 }
140}
141
142struct IdDeprecated(proc_macro2::TokenStream);
143
144impl Parse for IdDeprecated {
145 fn parse(input: ParseStream) -> Result<Self> {
146 parse_id(input, quote! { ::solana_sdk::pubkey::Pubkey }).map(Self)
147 }
148}
149
150impl ToTokens for IdDeprecated {
151 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
152 deprecated_id_to_tokens(&self.0, quote! { ::solana_sdk::pubkey::Pubkey }, tokens)
153 }
154}
155
156struct ProgramSdkId(proc_macro2::TokenStream);
157impl Parse for ProgramSdkId {
158 fn parse(input: ParseStream) -> Result<Self> {
159 parse_id(input, quote! { ::solana_program::pubkey::Pubkey }).map(Self)
160 }
161}
162
163impl ToTokens for ProgramSdkId {
164 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
165 id_to_tokens(&self.0, quote! { ::solana_program::pubkey::Pubkey }, tokens)
166 }
167}
168
169struct ProgramSdkIdDeprecated(proc_macro2::TokenStream);
170impl Parse for ProgramSdkIdDeprecated {
171 fn parse(input: ParseStream) -> Result<Self> {
172 parse_id(input, quote! { ::solana_program::pubkey::Pubkey }).map(Self)
173 }
174}
175
176impl ToTokens for ProgramSdkIdDeprecated {
177 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
178 deprecated_id_to_tokens(&self.0, quote! { ::solana_program::pubkey::Pubkey }, tokens)
179 }
180}
181
182#[allow(dead_code)] struct RespanInput {
184 to_respan: Path,
185 respan_using: Span,
186}
187
188impl Parse for RespanInput {
189 fn parse(input: ParseStream) -> Result<Self> {
190 let to_respan: Path = input.parse()?;
191 let _comma: Token![,] = input.parse()?;
192 let respan_tree: TokenTree = input.parse()?;
193 match respan_tree {
194 TokenTree::Group(g) if g.delimiter() == Delimiter::None => {
195 let ident: Ident = syn::parse2(g.stream())?;
196 Ok(RespanInput {
197 to_respan,
198 respan_using: ident.span(),
199 })
200 }
201 TokenTree::Ident(i) => Ok(RespanInput {
202 to_respan,
203 respan_using: i.span(),
204 }),
205 val => Err(syn::Error::new_spanned(
206 val,
207 "expected None-delimited group",
208 )),
209 }
210 }
211}
212
213#[rustversion::since(1.46.0)] #[proc_macro]
231pub fn respan(input: TokenStream) -> TokenStream {
232 let RespanInput {
235 to_respan,
236 respan_using,
237 } = parse_macro_input!(input as RespanInput);
238 let to_respan: proc_macro2::TokenStream = to_respan
240 .into_token_stream()
241 .into_iter()
242 .map(|mut t| {
243 let new_span: Span = t.span().resolved_at(respan_using);
245 t.set_span(new_span);
246 t
247 })
248 .collect();
249 TokenStream::from(to_respan)
250}
251
252#[proc_macro]
253pub fn pubkey(input: TokenStream) -> TokenStream {
254 let id = parse_macro_input!(input as SdkPubkey);
255 TokenStream::from(quote! {#id})
256}
257
258#[proc_macro]
259pub fn program_pubkey(input: TokenStream) -> TokenStream {
260 let id = parse_macro_input!(input as ProgramSdkPubkey);
261 TokenStream::from(quote! {#id})
262}
263
264#[proc_macro]
265pub fn declare_id(input: TokenStream) -> TokenStream {
266 let id = parse_macro_input!(input as Id);
267 TokenStream::from(quote! {#id})
268}
269
270#[proc_macro]
271pub fn declare_deprecated_id(input: TokenStream) -> TokenStream {
272 let id = parse_macro_input!(input as IdDeprecated);
273 TokenStream::from(quote! {#id})
274}
275
276#[proc_macro]
277pub fn program_declare_id(input: TokenStream) -> TokenStream {
278 let id = parse_macro_input!(input as ProgramSdkId);
279 TokenStream::from(quote! {#id})
280}
281
282#[proc_macro]
283pub fn program_declare_deprecated_id(input: TokenStream) -> TokenStream {
284 let id = parse_macro_input!(input as ProgramSdkIdDeprecated);
285 TokenStream::from(quote! {#id})
286}
287
288fn parse_pubkey(
289 id_literal: &LitStr,
290 pubkey_type: &proc_macro2::TokenStream,
291) -> Result<proc_macro2::TokenStream> {
292 let id_vec = bs58::decode(id_literal.value())
293 .into_vec()
294 .map_err(|_| syn::Error::new_spanned(id_literal, "failed to decode base58 string"))?;
295 let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| {
296 syn::Error::new_spanned(
297 id_literal,
298 format!("pubkey array is not 32 bytes long: len={}", id_vec.len()),
299 )
300 })?;
301 let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
302 Ok(quote! {
303 #pubkey_type::new_from_array(
304 [#(#bytes,)*]
305 )
306 })
307}
308
309struct Pubkeys {
310 method: Ident,
311 num: usize,
312 pubkeys: proc_macro2::TokenStream,
313}
314impl Parse for Pubkeys {
315 fn parse(input: ParseStream) -> Result<Self> {
316 let pubkey_type = quote! {
317 ::solana_sdk::pubkey::Pubkey
318 };
319
320 let method = input.parse()?;
321 let _comma: Token![,] = input.parse()?;
322 let (num, pubkeys) = if input.peek(syn::LitStr) {
323 let id_literal: LitStr = input.parse()?;
324 (1, parse_pubkey(&id_literal, &pubkey_type)?)
325 } else if input.peek(Bracket) {
326 let pubkey_strings;
327 bracketed!(pubkey_strings in input);
328 let punctuated: Punctuated<LitStr, Token![,]> =
329 Punctuated::parse_terminated(&pubkey_strings)?;
330 let mut pubkeys: Punctuated<proc_macro2::TokenStream, Token![,]> = Punctuated::new();
331 for string in punctuated.iter() {
332 pubkeys.push(parse_pubkey(string, &pubkey_type)?);
333 }
334 (pubkeys.len(), quote! {#pubkeys})
335 } else {
336 let stream: proc_macro2::TokenStream = input.parse()?;
337 return Err(syn::Error::new_spanned(stream, "unexpected token"));
338 };
339
340 Ok(Pubkeys {
341 method,
342 num,
343 pubkeys,
344 })
345 }
346}
347
348impl ToTokens for Pubkeys {
349 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
350 let Pubkeys {
351 method,
352 num,
353 pubkeys,
354 } = self;
355
356 let pubkey_type = quote! {
357 ::solana_sdk::pubkey::Pubkey
358 };
359 if *num == 1 {
360 tokens.extend(quote! {
361 pub fn #method() -> #pubkey_type {
362 #pubkeys
363 }
364 });
365 } else {
366 tokens.extend(quote! {
367 pub fn #method() -> ::std::vec::Vec<#pubkey_type> {
368 vec![#pubkeys]
369 }
370 });
371 }
372 }
373}
374
375#[proc_macro]
376pub fn pubkeys(input: TokenStream) -> TokenStream {
377 let pubkeys = parse_macro_input!(input as Pubkeys);
378 TokenStream::from(quote! {#pubkeys})
379}
380
381#[proc_macro_attribute]
384pub fn wasm_bindgen_stub(_attr: TokenStream, item: TokenStream) -> TokenStream {
385 match parse_macro_input!(item as syn::Item) {
386 syn::Item::Struct(mut item_struct) => {
387 if let syn::Fields::Named(fields) = &mut item_struct.fields {
388 for field in fields.named.iter_mut() {
391 field.attrs.retain(|attr| {
392 !attr
393 .path
394 .segments
395 .iter()
396 .any(|segment| segment.ident == "wasm_bindgen")
397 });
398 }
399 }
400 quote! { #item_struct }
401 }
402 item => {
403 quote!(#item)
404 }
405 }
406 .into()
407}