id.rs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. //! Copied from solana/sdk/macro so that Anchor programs don't need to specify
  2. //! `solana_program` as an additional crate dependency, but instead can access
  3. //! it via `anchor_lang::declare_id`.
  4. //!
  5. //! Convenience macro to declare a static public key and functions to interact with it
  6. //!
  7. //! Input: a single literal base58 string representation of a program's id
  8. extern crate proc_macro;
  9. use proc_macro2::{Delimiter, Span, TokenTree};
  10. use quote::{quote, ToTokens};
  11. use syn::{
  12. bracketed,
  13. parse::{Parse, ParseStream, Result},
  14. punctuated::Punctuated,
  15. token::Bracket,
  16. Expr, Ident, LitByte, LitStr, Path, Token,
  17. };
  18. fn parse_id(
  19. input: ParseStream,
  20. pubkey_type: proc_macro2::TokenStream,
  21. ) -> Result<proc_macro2::TokenStream> {
  22. let id = if input.peek(syn::LitStr) {
  23. let id_literal: LitStr = input.parse()?;
  24. parse_pubkey(&id_literal, &pubkey_type)?
  25. } else {
  26. let expr: Expr = input.parse()?;
  27. quote! { #expr }
  28. };
  29. if !input.is_empty() {
  30. let stream: proc_macro2::TokenStream = input.parse()?;
  31. return Err(syn::Error::new_spanned(stream, "unexpected token"));
  32. }
  33. Ok(id)
  34. }
  35. fn id_to_tokens(
  36. id: &proc_macro2::TokenStream,
  37. pubkey_type: proc_macro2::TokenStream,
  38. tokens: &mut proc_macro2::TokenStream,
  39. ) {
  40. tokens.extend(quote! {
  41. /// The static program ID
  42. pub static ID: #pubkey_type = #id;
  43. /// Const version of `ID`
  44. pub const ID_CONST: #pubkey_type = #id;
  45. /// Confirms that a given pubkey is equivalent to the program ID
  46. pub fn check_id(id: &#pubkey_type) -> bool {
  47. id == &ID
  48. }
  49. /// Returns the program ID
  50. pub fn id() -> #pubkey_type {
  51. ID
  52. }
  53. /// Const version of `ID`
  54. pub const fn id_const() -> #pubkey_type {
  55. ID_CONST
  56. }
  57. #[cfg(test)]
  58. #[test]
  59. fn test_id() {
  60. assert!(check_id(&id()));
  61. }
  62. });
  63. }
  64. fn deprecated_id_to_tokens(
  65. id: &proc_macro2::TokenStream,
  66. pubkey_type: proc_macro2::TokenStream,
  67. tokens: &mut proc_macro2::TokenStream,
  68. ) {
  69. tokens.extend(quote! {
  70. /// The static program ID
  71. pub static ID: #pubkey_type = #id;
  72. /// Confirms that a given pubkey is equivalent to the program ID
  73. #[deprecated()]
  74. pub fn check_id(id: &#pubkey_type) -> bool {
  75. id == &ID
  76. }
  77. /// Returns the program ID
  78. #[deprecated()]
  79. pub fn id() -> #pubkey_type {
  80. ID
  81. }
  82. #[cfg(test)]
  83. #[test]
  84. fn test_id() {
  85. #[allow(deprecated)]
  86. assert!(check_id(&id()));
  87. }
  88. });
  89. }
  90. pub struct Pubkey(proc_macro2::TokenStream);
  91. impl Parse for Pubkey {
  92. fn parse(input: ParseStream) -> Result<Self> {
  93. parse_id(
  94. input,
  95. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  96. )
  97. .map(Self)
  98. }
  99. }
  100. impl ToTokens for Pubkey {
  101. fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
  102. let id = &self.0;
  103. tokens.extend(quote! {#id})
  104. }
  105. }
  106. pub struct Id(proc_macro2::TokenStream);
  107. impl Parse for Id {
  108. fn parse(input: ParseStream) -> Result<Self> {
  109. parse_id(
  110. input,
  111. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  112. )
  113. .map(Self)
  114. }
  115. }
  116. impl ToTokens for Id {
  117. fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
  118. id_to_tokens(
  119. &self.0,
  120. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  121. tokens,
  122. )
  123. }
  124. }
  125. struct IdDeprecated(proc_macro2::TokenStream);
  126. impl Parse for IdDeprecated {
  127. fn parse(input: ParseStream) -> Result<Self> {
  128. parse_id(
  129. input,
  130. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  131. )
  132. .map(Self)
  133. }
  134. }
  135. impl ToTokens for IdDeprecated {
  136. fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
  137. deprecated_id_to_tokens(
  138. &self.0,
  139. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  140. tokens,
  141. )
  142. }
  143. }
  144. struct ProgramSdkId(proc_macro2::TokenStream);
  145. impl Parse for ProgramSdkId {
  146. fn parse(input: ParseStream) -> Result<Self> {
  147. parse_id(
  148. input,
  149. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  150. )
  151. .map(Self)
  152. }
  153. }
  154. impl ToTokens for ProgramSdkId {
  155. fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
  156. id_to_tokens(
  157. &self.0,
  158. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  159. tokens,
  160. )
  161. }
  162. }
  163. struct ProgramSdkIdDeprecated(proc_macro2::TokenStream);
  164. impl Parse for ProgramSdkIdDeprecated {
  165. fn parse(input: ParseStream) -> Result<Self> {
  166. parse_id(
  167. input,
  168. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  169. )
  170. .map(Self)
  171. }
  172. }
  173. impl ToTokens for ProgramSdkIdDeprecated {
  174. fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
  175. deprecated_id_to_tokens(
  176. &self.0,
  177. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  178. tokens,
  179. )
  180. }
  181. }
  182. #[allow(dead_code)] // `respan` may be compiled out
  183. struct RespanInput {
  184. to_respan: Path,
  185. respan_using: Span,
  186. }
  187. impl Parse for RespanInput {
  188. fn parse(input: ParseStream) -> Result<Self> {
  189. let to_respan: Path = input.parse()?;
  190. let _comma: Token![,] = input.parse()?;
  191. let respan_tree: TokenTree = input.parse()?;
  192. match respan_tree {
  193. TokenTree::Group(g) if g.delimiter() == Delimiter::None => {
  194. let ident: Ident = syn::parse2(g.stream())?;
  195. Ok(RespanInput {
  196. to_respan,
  197. respan_using: ident.span(),
  198. })
  199. }
  200. val => Err(syn::Error::new_spanned(
  201. val,
  202. "expected None-delimited group",
  203. )),
  204. }
  205. }
  206. }
  207. fn parse_pubkey(
  208. id_literal: &LitStr,
  209. pubkey_type: &proc_macro2::TokenStream,
  210. ) -> Result<proc_macro2::TokenStream> {
  211. let id_vec = bs58::decode(id_literal.value())
  212. .into_vec()
  213. .map_err(|_| syn::Error::new_spanned(id_literal, "failed to decode base58 string"))?;
  214. let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| {
  215. syn::Error::new_spanned(
  216. id_literal,
  217. format!("pubkey array is not 32 bytes long: len={}", id_vec.len()),
  218. )
  219. })?;
  220. let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
  221. Ok(quote! {
  222. #pubkey_type::new_from_array(
  223. [#(#bytes,)*]
  224. )
  225. })
  226. }
  227. struct Pubkeys {
  228. method: Ident,
  229. num: usize,
  230. pubkeys: proc_macro2::TokenStream,
  231. }
  232. impl Parse for Pubkeys {
  233. fn parse(input: ParseStream) -> Result<Self> {
  234. let pubkey_type = quote! {
  235. anchor_lang::solana_program::pubkey::Pubkey
  236. };
  237. let method = input.parse()?;
  238. let _comma: Token![,] = input.parse()?;
  239. let (num, pubkeys) = if input.peek(syn::LitStr) {
  240. let id_literal: LitStr = input.parse()?;
  241. (1, parse_pubkey(&id_literal, &pubkey_type)?)
  242. } else if input.peek(Bracket) {
  243. let pubkey_strings;
  244. bracketed!(pubkey_strings in input);
  245. let punctuated: Punctuated<LitStr, Token![,]> =
  246. Punctuated::parse_terminated(&pubkey_strings)?;
  247. let mut pubkeys: Punctuated<proc_macro2::TokenStream, Token![,]> = Punctuated::new();
  248. for string in punctuated.iter() {
  249. pubkeys.push(parse_pubkey(string, &pubkey_type)?);
  250. }
  251. (pubkeys.len(), quote! {#pubkeys})
  252. } else {
  253. let stream: proc_macro2::TokenStream = input.parse()?;
  254. return Err(syn::Error::new_spanned(stream, "unexpected token"));
  255. };
  256. Ok(Pubkeys {
  257. method,
  258. num,
  259. pubkeys,
  260. })
  261. }
  262. }
  263. impl ToTokens for Pubkeys {
  264. fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
  265. let Pubkeys {
  266. method,
  267. num,
  268. pubkeys,
  269. } = self;
  270. let pubkey_type = quote! {
  271. anchor_lang::solana_program::pubkey::Pubkey
  272. };
  273. if *num == 1 {
  274. tokens.extend(quote! {
  275. pub fn #method() -> #pubkey_type {
  276. #pubkeys
  277. }
  278. });
  279. } else {
  280. tokens.extend(quote! {
  281. pub fn #method() -> ::std::vec::Vec<#pubkey_type> {
  282. vec![#pubkeys]
  283. }
  284. });
  285. }
  286. }
  287. }