1extern crate proc_macro;
6
7use {
8 proc_macro::TokenStream,
9 proc_macro2::Span,
10 quote::{quote, ToTokens},
11 syn::{
12 bracketed,
13 parse::{Parse, ParseStream, Result},
14 parse_macro_input,
15 punctuated::Punctuated,
16 token::Bracket,
17 Expr, Ident, LitByte, LitStr, Token,
18 },
19};
20
21fn parse_id(
22 input: ParseStream,
23 pubkey_type: proc_macro2::TokenStream,
24) -> Result<proc_macro2::TokenStream> {
25 let id = if input.peek(syn::LitStr) {
26 let id_literal: LitStr = input.parse()?;
27 parse_pubkey(&id_literal, &pubkey_type)?
28 } else {
29 let expr: Expr = input.parse()?;
30 quote! { #expr }
31 };
32
33 if !input.is_empty() {
34 let stream: proc_macro2::TokenStream = input.parse()?;
35 return Err(syn::Error::new_spanned(stream, "unexpected token"));
36 }
37 Ok(id)
38}
39
40fn id_to_tokens(
41 id: &proc_macro2::TokenStream,
42 pubkey_type: proc_macro2::TokenStream,
43 tokens: &mut proc_macro2::TokenStream,
44) {
45 tokens.extend(quote! {
46 pub const ID: #pubkey_type = #id;
48
49 pub fn check_id(id: &#pubkey_type) -> bool {
53 id == &ID
54 }
55
56 pub const fn id() -> #pubkey_type {
58 ID
59 }
60
61 #[cfg(test)]
62 #[test]
63 fn test_id() {
64 assert!(check_id(&id()));
65 }
66 });
67}
68
69fn deprecated_id_to_tokens(
70 id: &proc_macro2::TokenStream,
71 pubkey_type: proc_macro2::TokenStream,
72 tokens: &mut proc_macro2::TokenStream,
73) {
74 tokens.extend(quote! {
75 pub static ID: #pubkey_type = #id;
77
78 #[deprecated()]
80 pub fn check_id(id: &#pubkey_type) -> bool {
81 id == &ID
82 }
83
84 #[deprecated()]
86 pub fn id() -> #pubkey_type {
87 ID
88 }
89
90 #[cfg(test)]
91 #[test]
92 #[allow(deprecated)]
93 fn test_id() {
94 assert!(check_id(&id()));
95 }
96 });
97}
98
99struct SdkPubkey(proc_macro2::TokenStream);
100
101impl Parse for SdkPubkey {
102 fn parse(input: ParseStream) -> Result<Self> {
103 parse_id(input, quote! { ::clone_solana_sdk::pubkey::Pubkey }).map(Self)
104 }
105}
106
107impl ToTokens for SdkPubkey {
108 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
109 let id = &self.0;
110 tokens.extend(quote! {#id})
111 }
112}
113
114struct ProgramSdkPubkey(proc_macro2::TokenStream);
115
116impl Parse for ProgramSdkPubkey {
117 fn parse(input: ParseStream) -> Result<Self> {
118 parse_id(input, quote! { ::clone_solana_program::pubkey::Pubkey }).map(Self)
119 }
120}
121
122impl ToTokens for ProgramSdkPubkey {
123 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
124 let id = &self.0;
125 tokens.extend(quote! {#id})
126 }
127}
128
129struct Id(proc_macro2::TokenStream);
130
131impl Parse for Id {
132 fn parse(input: ParseStream) -> Result<Self> {
133 parse_id(input, quote! { ::clone_solana_sdk::pubkey::Pubkey }).map(Self)
134 }
135}
136
137impl ToTokens for Id {
138 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
139 id_to_tokens(
140 &self.0,
141 quote! { ::clone_solana_sdk::pubkey::Pubkey },
142 tokens,
143 )
144 }
145}
146
147struct IdDeprecated(proc_macro2::TokenStream);
148
149impl Parse for IdDeprecated {
150 fn parse(input: ParseStream) -> Result<Self> {
151 parse_id(input, quote! { ::clone_solana_sdk::pubkey::Pubkey }).map(Self)
152 }
153}
154
155impl ToTokens for IdDeprecated {
156 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
157 deprecated_id_to_tokens(
158 &self.0,
159 quote! { ::clone_solana_sdk::pubkey::Pubkey },
160 tokens,
161 )
162 }
163}
164
165struct ProgramSdkId(proc_macro2::TokenStream);
166impl Parse for ProgramSdkId {
167 fn parse(input: ParseStream) -> Result<Self> {
168 parse_id(input, quote! { ::clone_solana_program::pubkey::Pubkey }).map(Self)
169 }
170}
171
172impl ToTokens for ProgramSdkId {
173 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
174 id_to_tokens(
175 &self.0,
176 quote! { ::clone_solana_program::pubkey::Pubkey },
177 tokens,
178 )
179 }
180}
181
182struct ProgramSdkIdDeprecated(proc_macro2::TokenStream);
183impl Parse for ProgramSdkIdDeprecated {
184 fn parse(input: ParseStream) -> Result<Self> {
185 parse_id(input, quote! { ::clone_solana_program::pubkey::Pubkey }).map(Self)
186 }
187}
188
189impl ToTokens for ProgramSdkIdDeprecated {
190 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
191 deprecated_id_to_tokens(
192 &self.0,
193 quote! { ::clone_solana_program::pubkey::Pubkey },
194 tokens,
195 )
196 }
197}
198
199#[deprecated(since = "2.1.0", note = "Use `clone_solana_pubkey::pubkey` instead")]
200#[proc_macro]
201pub fn pubkey(input: TokenStream) -> TokenStream {
202 let id = parse_macro_input!(input as SdkPubkey);
203 TokenStream::from(quote! {#id})
204}
205
206#[deprecated(since = "2.1.0", note = "Use `clone_solana_pubkey::pubkey!` instead")]
207#[proc_macro]
208pub fn program_pubkey(input: TokenStream) -> TokenStream {
209 let id = parse_macro_input!(input as ProgramSdkPubkey);
210 TokenStream::from(quote! {#id})
211}
212
213#[proc_macro]
214pub fn declare_id(input: TokenStream) -> TokenStream {
215 let id = parse_macro_input!(input as Id);
216 TokenStream::from(quote! {#id})
217}
218
219#[proc_macro]
220pub fn declare_deprecated_id(input: TokenStream) -> TokenStream {
221 let id = parse_macro_input!(input as IdDeprecated);
222 TokenStream::from(quote! {#id})
223}
224
225#[deprecated(
226 since = "2.1.0",
227 note = "Use `clone_solana_pubkey::declare_id` instead"
228)]
229#[proc_macro]
230pub fn program_declare_id(input: TokenStream) -> TokenStream {
231 let id = parse_macro_input!(input as ProgramSdkId);
232 TokenStream::from(quote! {#id})
233}
234
235#[deprecated(
236 since = "2.1.0",
237 note = "Use `clone_solana_pubkey::declare_deprecated_id` instead"
238)]
239#[proc_macro]
240pub fn program_declare_deprecated_id(input: TokenStream) -> TokenStream {
241 let id = parse_macro_input!(input as ProgramSdkIdDeprecated);
242 TokenStream::from(quote! {#id})
243}
244
245fn parse_pubkey(
246 id_literal: &LitStr,
247 pubkey_type: &proc_macro2::TokenStream,
248) -> Result<proc_macro2::TokenStream> {
249 let id_vec = bs58::decode(id_literal.value())
250 .into_vec()
251 .map_err(|_| syn::Error::new_spanned(id_literal, "failed to decode base58 string"))?;
252 let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| {
253 syn::Error::new_spanned(
254 id_literal,
255 format!("pubkey array is not 32 bytes long: len={}", id_vec.len()),
256 )
257 })?;
258 let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
259 Ok(quote! {
260 #pubkey_type::new_from_array(
261 [#(#bytes,)*]
262 )
263 })
264}
265
266struct Pubkeys {
267 method: Ident,
268 num: usize,
269 pubkeys: proc_macro2::TokenStream,
270}
271impl Parse for Pubkeys {
272 fn parse(input: ParseStream) -> Result<Self> {
273 let pubkey_type = quote! {
274 ::clone_solana_sdk::pubkey::Pubkey
275 };
276
277 let method = input.parse()?;
278 let _comma: Token![,] = input.parse()?;
279 let (num, pubkeys) = if input.peek(syn::LitStr) {
280 let id_literal: LitStr = input.parse()?;
281 (1, parse_pubkey(&id_literal, &pubkey_type)?)
282 } else if input.peek(Bracket) {
283 let pubkey_strings;
284 bracketed!(pubkey_strings in input);
285 let punctuated: Punctuated<LitStr, Token![,]> =
286 Punctuated::parse_terminated(&pubkey_strings)?;
287 let mut pubkeys: Punctuated<proc_macro2::TokenStream, Token![,]> = Punctuated::new();
288 for string in punctuated.iter() {
289 pubkeys.push(parse_pubkey(string, &pubkey_type)?);
290 }
291 (pubkeys.len(), quote! {#pubkeys})
292 } else {
293 let stream: proc_macro2::TokenStream = input.parse()?;
294 return Err(syn::Error::new_spanned(stream, "unexpected token"));
295 };
296
297 Ok(Pubkeys {
298 method,
299 num,
300 pubkeys,
301 })
302 }
303}
304
305impl ToTokens for Pubkeys {
306 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
307 let Pubkeys {
308 method,
309 num,
310 pubkeys,
311 } = self;
312
313 let pubkey_type = quote! {
314 ::clone_solana_sdk::pubkey::Pubkey
315 };
316 if *num == 1 {
317 tokens.extend(quote! {
318 pub fn #method() -> #pubkey_type {
319 #pubkeys
320 }
321 });
322 } else {
323 tokens.extend(quote! {
324 pub fn #method() -> ::std::vec::Vec<#pubkey_type> {
325 vec![#pubkeys]
326 }
327 });
328 }
329 }
330}
331
332#[proc_macro]
333pub fn pubkeys(input: TokenStream) -> TokenStream {
334 let pubkeys = parse_macro_input!(input as Pubkeys);
335 TokenStream::from(quote! {#pubkeys})
336}
337
338#[proc_macro_derive(CloneZeroed)]
341pub fn derive_clone_zeroed(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
342 match parse_macro_input!(input as syn::Item) {
343 syn::Item::Struct(item_struct) => {
344 let clone_statements = match item_struct.fields {
345 syn::Fields::Named(ref fields) => fields.named.iter().map(|f| {
346 let name = &f.ident;
347 quote! {
348 core::ptr::addr_of_mut!((*ptr).#name).write(self.#name);
349 }
350 }),
351 _ => unimplemented!(),
352 };
353 let name = &item_struct.ident;
354 quote! {
355 impl Clone for #name {
356 fn clone(&self) -> Self {
361 let mut value = core::mem::MaybeUninit::<Self>::uninit();
362 unsafe {
363 core::ptr::write_bytes(&mut value, 0, 1);
364 let ptr = value.as_mut_ptr();
365 #(#clone_statements)*
366 value.assume_init()
367 }
368 }
369 }
370 }
371 }
372 _ => unimplemented!(),
373 }
374 .into()
375}