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 const ID: #pubkey_type = #id;
49
50 pub fn check_id(id: &#pubkey_type) -> bool {
54 id == &ID
55 }
56
57 pub const fn id() -> #pubkey_type {
59 ID
60 }
61
62 #[cfg(test)]
63 #[test]
64 fn test_id() {
65 assert!(check_id(&id()));
66 }
67 });
68}
69
70fn deprecated_id_to_tokens(
71 id: &proc_macro2::TokenStream,
72 pubkey_type: proc_macro2::TokenStream,
73 tokens: &mut proc_macro2::TokenStream,
74) {
75 tokens.extend(quote! {
76 pub static ID: #pubkey_type = #id;
78
79 #[deprecated()]
81 pub fn check_id(id: &#pubkey_type) -> bool {
82 id == &ID
83 }
84
85 #[deprecated()]
87 pub fn id() -> #pubkey_type {
88 ID
89 }
90
91 #[cfg(test)]
92 #[test]
93 #[allow(deprecated)]
94 fn test_id() {
95 assert!(check_id(&id()));
96 }
97 });
98}
99
100struct SdkPubkey(proc_macro2::TokenStream);
101
102impl Parse for SdkPubkey {
103 fn parse(input: ParseStream) -> Result<Self> {
104 parse_id(input, quote! { ::miraland_sdk::pubkey::Pubkey }).map(Self)
105 }
106}
107
108impl ToTokens for SdkPubkey {
109 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
110 let id = &self.0;
111 tokens.extend(quote! {#id})
112 }
113}
114
115struct ProgramSdkPubkey(proc_macro2::TokenStream);
116
117impl Parse for ProgramSdkPubkey {
118 fn parse(input: ParseStream) -> Result<Self> {
119 parse_id(input, quote! { ::miraland_program::pubkey::Pubkey }).map(Self)
120 }
121}
122
123impl ToTokens for ProgramSdkPubkey {
124 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
125 let id = &self.0;
126 tokens.extend(quote! {#id})
127 }
128}
129
130struct Id(proc_macro2::TokenStream);
131
132impl Parse for Id {
133 fn parse(input: ParseStream) -> Result<Self> {
134 parse_id(input, quote! { ::miraland_sdk::pubkey::Pubkey }).map(Self)
135 }
136}
137
138impl ToTokens for Id {
139 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
140 id_to_tokens(&self.0, quote! { ::miraland_sdk::pubkey::Pubkey }, tokens)
141 }
142}
143
144struct IdDeprecated(proc_macro2::TokenStream);
145
146impl Parse for IdDeprecated {
147 fn parse(input: ParseStream) -> Result<Self> {
148 parse_id(input, quote! { ::miraland_sdk::pubkey::Pubkey }).map(Self)
149 }
150}
151
152impl ToTokens for IdDeprecated {
153 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
154 deprecated_id_to_tokens(&self.0, quote! { ::miraland_sdk::pubkey::Pubkey }, tokens)
155 }
156}
157
158struct ProgramSdkId(proc_macro2::TokenStream);
159impl Parse for ProgramSdkId {
160 fn parse(input: ParseStream) -> Result<Self> {
161 parse_id(input, quote! { ::miraland_program::pubkey::Pubkey }).map(Self)
162 }
163}
164
165impl ToTokens for ProgramSdkId {
166 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
167 id_to_tokens(&self.0, quote! { ::miraland_program::pubkey::Pubkey }, tokens)
168 }
169}
170
171struct ProgramSdkIdDeprecated(proc_macro2::TokenStream);
172impl Parse for ProgramSdkIdDeprecated {
173 fn parse(input: ParseStream) -> Result<Self> {
174 parse_id(input, quote! { ::miraland_program::pubkey::Pubkey }).map(Self)
175 }
176}
177
178impl ToTokens for ProgramSdkIdDeprecated {
179 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
180 deprecated_id_to_tokens(&self.0, quote! { ::miraland_program::pubkey::Pubkey }, tokens)
181 }
182}
183
184#[allow(dead_code)] struct RespanInput {
186 to_respan: Path,
187 respan_using: Span,
188}
189
190impl Parse for RespanInput {
191 fn parse(input: ParseStream) -> Result<Self> {
192 let to_respan: Path = input.parse()?;
193 let _comma: Token![,] = input.parse()?;
194 let respan_tree: TokenTree = input.parse()?;
195 match respan_tree {
196 TokenTree::Group(g) if g.delimiter() == Delimiter::None => {
197 let ident: Ident = syn::parse2(g.stream())?;
198 Ok(RespanInput {
199 to_respan,
200 respan_using: ident.span(),
201 })
202 }
203 TokenTree::Ident(i) => Ok(RespanInput {
204 to_respan,
205 respan_using: i.span(),
206 }),
207 val => Err(syn::Error::new_spanned(
208 val,
209 "expected None-delimited group",
210 )),
211 }
212 }
213}
214
215#[rustversion::since(1.46.0)] #[proc_macro]
233pub fn respan(input: TokenStream) -> TokenStream {
234 let RespanInput {
237 to_respan,
238 respan_using,
239 } = parse_macro_input!(input as RespanInput);
240 let to_respan: proc_macro2::TokenStream = to_respan
242 .into_token_stream()
243 .into_iter()
244 .map(|mut t| {
245 let new_span: Span = t.span().resolved_at(respan_using);
247 t.set_span(new_span);
248 t
249 })
250 .collect();
251 TokenStream::from(to_respan)
252}
253
254#[proc_macro]
255pub fn pubkey(input: TokenStream) -> TokenStream {
256 let id = parse_macro_input!(input as SdkPubkey);
257 TokenStream::from(quote! {#id})
258}
259
260#[proc_macro]
261pub fn program_pubkey(input: TokenStream) -> TokenStream {
262 let id = parse_macro_input!(input as ProgramSdkPubkey);
263 TokenStream::from(quote! {#id})
264}
265
266#[proc_macro]
267pub fn declare_id(input: TokenStream) -> TokenStream {
268 let id = parse_macro_input!(input as Id);
269 TokenStream::from(quote! {#id})
270}
271
272#[proc_macro]
273pub fn declare_deprecated_id(input: TokenStream) -> TokenStream {
274 let id = parse_macro_input!(input as IdDeprecated);
275 TokenStream::from(quote! {#id})
276}
277
278#[proc_macro]
279pub fn program_declare_id(input: TokenStream) -> TokenStream {
280 let id = parse_macro_input!(input as ProgramSdkId);
281 TokenStream::from(quote! {#id})
282}
283
284#[proc_macro]
285pub fn program_declare_deprecated_id(input: TokenStream) -> TokenStream {
286 let id = parse_macro_input!(input as ProgramSdkIdDeprecated);
287 TokenStream::from(quote! {#id})
288}
289
290fn parse_pubkey(
291 id_literal: &LitStr,
292 pubkey_type: &proc_macro2::TokenStream,
293) -> Result<proc_macro2::TokenStream> {
294 let id_vec = bs58::decode(id_literal.value())
295 .into_vec()
296 .map_err(|_| syn::Error::new_spanned(id_literal, "failed to decode base58 string"))?;
297 let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| {
298 syn::Error::new_spanned(
299 id_literal,
300 format!("pubkey array is not 32 bytes long: len={}", id_vec.len()),
301 )
302 })?;
303 let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
304 Ok(quote! {
305 #pubkey_type::new_from_array(
306 [#(#bytes,)*]
307 )
308 })
309}
310
311struct Pubkeys {
312 method: Ident,
313 num: usize,
314 pubkeys: proc_macro2::TokenStream,
315}
316impl Parse for Pubkeys {
317 fn parse(input: ParseStream) -> Result<Self> {
318 let pubkey_type = quote! {
319 ::miraland_sdk::pubkey::Pubkey
320 };
321
322 let method = input.parse()?;
323 let _comma: Token![,] = input.parse()?;
324 let (num, pubkeys) = if input.peek(syn::LitStr) {
325 let id_literal: LitStr = input.parse()?;
326 (1, parse_pubkey(&id_literal, &pubkey_type)?)
327 } else if input.peek(Bracket) {
328 let pubkey_strings;
329 bracketed!(pubkey_strings in input);
330 let punctuated: Punctuated<LitStr, Token![,]> =
331 Punctuated::parse_terminated(&pubkey_strings)?;
332 let mut pubkeys: Punctuated<proc_macro2::TokenStream, Token![,]> = Punctuated::new();
333 for string in punctuated.iter() {
334 pubkeys.push(parse_pubkey(string, &pubkey_type)?);
335 }
336 (pubkeys.len(), quote! {#pubkeys})
337 } else {
338 let stream: proc_macro2::TokenStream = input.parse()?;
339 return Err(syn::Error::new_spanned(stream, "unexpected token"));
340 };
341
342 Ok(Pubkeys {
343 method,
344 num,
345 pubkeys,
346 })
347 }
348}
349
350impl ToTokens for Pubkeys {
351 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
352 let Pubkeys {
353 method,
354 num,
355 pubkeys,
356 } = self;
357
358 let pubkey_type = quote! {
359 ::miraland_sdk::pubkey::Pubkey
360 };
361 if *num == 1 {
362 tokens.extend(quote! {
363 pub fn #method() -> #pubkey_type {
364 #pubkeys
365 }
366 });
367 } else {
368 tokens.extend(quote! {
369 pub fn #method() -> ::std::vec::Vec<#pubkey_type> {
370 vec![#pubkeys]
371 }
372 });
373 }
374 }
375}
376
377#[proc_macro]
378pub fn pubkeys(input: TokenStream) -> TokenStream {
379 let pubkeys = parse_macro_input!(input as Pubkeys);
380 TokenStream::from(quote! {#pubkeys})
381}
382
383#[proc_macro_attribute]
386pub fn wasm_bindgen_stub(_attr: TokenStream, item: TokenStream) -> TokenStream {
387 match parse_macro_input!(item as syn::Item) {
388 syn::Item::Struct(mut item_struct) => {
389 if let syn::Fields::Named(fields) = &mut item_struct.fields {
390 for field in fields.named.iter_mut() {
393 field.attrs.retain(|attr| {
394 !attr
395 .path()
396 .segments
397 .iter()
398 .any(|segment| segment.ident == "wasm_bindgen")
399 });
400 }
401 }
402 quote! { #item_struct }
403 }
404 item => {
405 quote!(#item)
406 }
407 }
408 .into()
409}
410
411#[proc_macro_derive(CloneZeroed)]
414pub fn derive_clone_zeroed(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
415 match parse_macro_input!(input as syn::Item) {
416 syn::Item::Struct(item_struct) => {
417 let clone_statements = match item_struct.fields {
418 syn::Fields::Named(ref fields) => fields.named.iter().map(|f| {
419 let name = &f.ident;
420 quote! {
421 std::ptr::addr_of_mut!((*ptr).#name).write(self.#name);
422 }
423 }),
424 _ => unimplemented!(),
425 };
426 let name = &item_struct.ident;
427 quote! {
428 impl Clone for #name {
429 fn clone(&self) -> Self {
434 let mut value = std::mem::MaybeUninit::<Self>::uninit();
435 unsafe {
436 std::ptr::write_bytes(&mut value, 0, 1);
437 let ptr = value.as_mut_ptr();
438 #(#clone_statements)*
439 value.assume_init()
440 }
441 }
442 }
443 }
444 }
445 _ => unimplemented!(),
446 }
447 .into()
448}