gemachain_sdk_macro/
lib.rs1extern crate proc_macro;
6
7use proc_macro::TokenStream;
8use proc_macro2::{Delimiter, Span, TokenTree};
9use quote::{quote, ToTokens};
10use std::convert::TryFrom;
11use syn::{
12 bracketed,
13 parse::{Parse, ParseStream, Result},
14 parse_macro_input,
15 punctuated::Punctuated,
16 token::Bracket,
17 Expr, Ident, LitByte, LitStr, Path, Token,
18};
19
20fn parse_id(
21 input: ParseStream,
22 pubkey_type: proc_macro2::TokenStream,
23) -> Result<proc_macro2::TokenStream> {
24 let id = if input.peek(syn::LitStr) {
25 let id_literal: LitStr = input.parse()?;
26 parse_pubkey(&id_literal, &pubkey_type)?
27 } else {
28 let expr: Expr = input.parse()?;
29 quote! { #expr }
30 };
31
32 if !input.is_empty() {
33 let stream: proc_macro2::TokenStream = input.parse()?;
34 return Err(syn::Error::new_spanned(stream, "unexpected token"));
35 }
36 Ok(id)
37}
38
39fn id_to_tokens(
40 id: &proc_macro2::TokenStream,
41 pubkey_type: proc_macro2::TokenStream,
42 tokens: &mut proc_macro2::TokenStream,
43) {
44 tokens.extend(quote! {
45 pub static ID: #pubkey_type = #id;
47
48 pub fn check_id(id: &#pubkey_type) -> bool {
50 id == &ID
51 }
52
53 pub fn id() -> #pubkey_type {
55 ID
56 }
57
58 #[cfg(test)]
59 #[test]
60 fn test_id() {
61 assert!(check_id(&id()));
62 }
63 });
64}
65
66fn deprecated_id_to_tokens(
67 id: &proc_macro2::TokenStream,
68 pubkey_type: proc_macro2::TokenStream,
69 tokens: &mut proc_macro2::TokenStream,
70) {
71 tokens.extend(quote! {
72 pub static ID: #pubkey_type = #id;
74
75 #[deprecated()]
77 pub fn check_id(id: &#pubkey_type) -> bool {
78 id == &ID
79 }
80
81 #[deprecated()]
83 pub fn id() -> #pubkey_type {
84 ID
85 }
86
87 #[cfg(test)]
88 #[test]
89 fn test_id() {
90 #[allow(deprecated)]
91 assert!(check_id(&id()));
92 }
93 });
94}
95
96struct Id(proc_macro2::TokenStream);
97
98impl Parse for Id {
99 fn parse(input: ParseStream) -> Result<Self> {
100 parse_id(input, quote! { ::gemachain_sdk::pubkey::Pubkey }).map(Self)
101 }
102}
103
104impl ToTokens for Id {
105 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
106 id_to_tokens(&self.0, quote! { ::gemachain_sdk::pubkey::Pubkey }, tokens)
107 }
108}
109
110struct IdDeprecated(proc_macro2::TokenStream);
111
112impl Parse for IdDeprecated {
113 fn parse(input: ParseStream) -> Result<Self> {
114 parse_id(input, quote! { ::gemachain_sdk::pubkey::Pubkey }).map(Self)
115 }
116}
117
118impl ToTokens for IdDeprecated {
119 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
120 deprecated_id_to_tokens(&self.0, quote! { ::gemachain_sdk::pubkey::Pubkey }, tokens)
121 }
122}
123
124struct ProgramSdkId(proc_macro2::TokenStream);
125impl Parse for ProgramSdkId {
126 fn parse(input: ParseStream) -> Result<Self> {
127 parse_id(input, quote! { ::gemachain_program::pubkey::Pubkey }).map(Self)
128 }
129}
130
131impl ToTokens for ProgramSdkId {
132 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
133 id_to_tokens(&self.0, quote! { ::gemachain_program::pubkey::Pubkey }, tokens)
134 }
135}
136
137struct ProgramSdkIdDeprecated(proc_macro2::TokenStream);
138impl Parse for ProgramSdkIdDeprecated {
139 fn parse(input: ParseStream) -> Result<Self> {
140 parse_id(input, quote! { ::gemachain_program::pubkey::Pubkey }).map(Self)
141 }
142}
143
144impl ToTokens for ProgramSdkIdDeprecated {
145 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
146 deprecated_id_to_tokens(&self.0, quote! { ::gemachain_program::pubkey::Pubkey }, tokens)
147 }
148}
149
150#[allow(dead_code)] struct RespanInput {
152 to_respan: Path,
153 respan_using: Span,
154}
155
156impl Parse for RespanInput {
157 fn parse(input: ParseStream) -> Result<Self> {
158 let to_respan: Path = input.parse()?;
159 let _comma: Token![,] = input.parse()?;
160 let respan_tree: TokenTree = input.parse()?;
161 match respan_tree {
162 TokenTree::Group(g) if g.delimiter() == Delimiter::None => {
163 let ident: Ident = syn::parse2(g.stream())?;
164 Ok(RespanInput {
165 to_respan,
166 respan_using: ident.span(),
167 })
168 }
169 val => Err(syn::Error::new_spanned(
170 val,
171 "expected None-delimited group",
172 )),
173 }
174 }
175}
176
177#[rustversion::since(1.46.0)] #[proc_macro]
195pub fn respan(input: TokenStream) -> TokenStream {
196 let RespanInput {
199 to_respan,
200 respan_using,
201 } = parse_macro_input!(input as RespanInput);
202 let to_respan: proc_macro2::TokenStream = to_respan
204 .into_token_stream()
205 .into_iter()
206 .map(|mut t| {
207 let new_span: Span = t.span().resolved_at(respan_using);
209 t.set_span(new_span);
210 t
211 })
212 .collect();
213 TokenStream::from(to_respan)
214}
215
216#[proc_macro]
217pub fn declare_id(input: TokenStream) -> TokenStream {
218 let id = parse_macro_input!(input as Id);
219 TokenStream::from(quote! {#id})
220}
221
222#[proc_macro]
223pub fn declare_deprecated_id(input: TokenStream) -> TokenStream {
224 let id = parse_macro_input!(input as IdDeprecated);
225 TokenStream::from(quote! {#id})
226}
227
228#[proc_macro]
229pub fn program_declare_id(input: TokenStream) -> TokenStream {
230 let id = parse_macro_input!(input as ProgramSdkId);
231 TokenStream::from(quote! {#id})
232}
233
234#[proc_macro]
235pub fn program_declare_deprecated_id(input: TokenStream) -> TokenStream {
236 let id = parse_macro_input!(input as ProgramSdkIdDeprecated);
237 TokenStream::from(quote! {#id})
238}
239
240fn parse_pubkey(
241 id_literal: &LitStr,
242 pubkey_type: &proc_macro2::TokenStream,
243) -> Result<proc_macro2::TokenStream> {
244 let id_vec = bs58::decode(id_literal.value())
245 .into_vec()
246 .map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?;
247 let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| {
248 syn::Error::new_spanned(
249 &id_literal,
250 format!("pubkey array is not 32 bytes long: len={}", id_vec.len()),
251 )
252 })?;
253 let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
254 Ok(quote! {
255 #pubkey_type::new_from_array(
256 [#(#bytes,)*]
257 )
258 })
259}
260
261struct Pubkeys {
262 method: Ident,
263 num: usize,
264 pubkeys: proc_macro2::TokenStream,
265}
266impl Parse for Pubkeys {
267 fn parse(input: ParseStream) -> Result<Self> {
268 let pubkey_type = quote! {
269 ::gemachain_sdk::pubkey::Pubkey
270 };
271
272 let method = input.parse()?;
273 let _comma: Token![,] = input.parse()?;
274 let (num, pubkeys) = if input.peek(syn::LitStr) {
275 let id_literal: LitStr = input.parse()?;
276 (1, parse_pubkey(&id_literal, &pubkey_type)?)
277 } else if input.peek(Bracket) {
278 let pubkey_strings;
279 bracketed!(pubkey_strings in input);
280 let punctuated: Punctuated<LitStr, Token![,]> =
281 Punctuated::parse_terminated(&pubkey_strings)?;
282 let mut pubkeys: Punctuated<proc_macro2::TokenStream, Token![,]> = Punctuated::new();
283 for string in punctuated.iter() {
284 pubkeys.push(parse_pubkey(string, &pubkey_type)?);
285 }
286 (pubkeys.len(), quote! {#pubkeys})
287 } else {
288 let stream: proc_macro2::TokenStream = input.parse()?;
289 return Err(syn::Error::new_spanned(stream, "unexpected token"));
290 };
291
292 Ok(Pubkeys {
293 method,
294 num,
295 pubkeys,
296 })
297 }
298}
299
300impl ToTokens for Pubkeys {
301 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
302 let Pubkeys {
303 method,
304 num,
305 pubkeys,
306 } = self;
307
308 let pubkey_type = quote! {
309 ::gemachain_sdk::pubkey::Pubkey
310 };
311 if *num == 1 {
312 tokens.extend(quote! {
313 pub fn #method() -> #pubkey_type {
314 #pubkeys
315 }
316 });
317 } else {
318 tokens.extend(quote! {
319 pub fn #method() -> ::std::vec::Vec<#pubkey_type> {
320 vec![#pubkeys]
321 }
322 });
323 }
324 }
325}
326
327#[proc_macro]
328pub fn pubkeys(input: TokenStream) -> TokenStream {
329 let pubkeys = parse_macro_input!(input as Pubkeys);
330 TokenStream::from(quote! {#pubkeys})
331}