concordium_std_derive/
lib.rs

1extern crate proc_macro;
2
3use concordium_contracts_common::{
4    AccountAddress, ContractAddress, ModuleReference, PublicKeyEcdsaSecp256k1, PublicKeyEd25519,
5    SignatureEcdsaSecp256k1, SignatureEd25519,
6};
7use proc_macro::TokenStream;
8use proc_macro2::{Group, Punct, Spacing, Span};
9use quote::{quote, ToTokens};
10use std::str::FromStr;
11use syn::LitStr;
12
13// Helper functions:
14
15// Tokenizes a slice of bytes.
16fn tokenize_slice(slice: &[u8]) -> proc_macro2::TokenStream {
17    let mut t = proc_macro2::TokenStream::new();
18    for byte in slice {
19        byte.to_tokens(&mut t);
20        Punct::new(',', Spacing::Alone).to_tokens(&mut t);
21    }
22    Group::new(proc_macro2::Delimiter::Bracket, t).to_token_stream()
23}
24
25// Parses the input tokens of a proc macro and returns the given error
26// message on parse error.
27fn parse_input(item: TokenStream, msg: &str) -> syn::Result<LitStr> {
28    match syn::parse::<LitStr>(item) {
29        Ok(string_literal) => Ok(string_literal),
30        Err(_) => Err(syn::Error::new(Span::call_site(), msg)),
31    }
32}
33
34// Parses the given tokens and transforms them using the provided worker
35// function. If parsing fails, then an error with the given message is produced.
36fn get_token_res(
37    item: TokenStream,
38    msg: &str,
39    worker_fun: impl FnOnce(String, Span) -> syn::Result<TokenStream>,
40) -> syn::Result<TokenStream> {
41    let input = parse_input(item, msg)?;
42    worker_fun(input.value(), input.span())
43}
44
45// Parses the given tokens, looks up an environment variable with the parsed
46// input as key and transforms the corresponding value using the provided worker
47// function. If parsing fails, then an error with the given message is produced.
48fn get_token_res_env(
49    item: TokenStream,
50    msg: &str,
51    worker_fun: impl FnOnce(String, Span) -> syn::Result<TokenStream>,
52) -> syn::Result<TokenStream> {
53    let input = parse_input(item, msg)?;
54    let environment_var_value = match std::env::var(input.value()) {
55        Ok(value) => value,
56        Err(e) => {
57            return Err(syn::Error::new(
58                input.span(),
59                format!("Environment variable error: {:?}", e),
60            ))
61        }
62    };
63    worker_fun(environment_var_value, input.span())
64}
65
66fn unwrap_or_report(res: syn::Result<TokenStream>) -> TokenStream {
67    res.unwrap_or_else(|e| e.into_compile_error().into())
68}
69
70// Worker functions
71
72fn acc_address_worker(str: String, span: Span) -> syn::Result<TokenStream> {
73    let address = match AccountAddress::from_str(&str) {
74        Ok(addr) => tokenize_slice(&addr.0),
75        Err(e) => return Err(syn::Error::new(span, format!("Invalid account address: {}", e))),
76    };
77
78    Ok(quote!(concordium_std::AccountAddress(#address)).into())
79}
80
81fn pubkey_ed25519_worker(str: String, span: Span) -> syn::Result<TokenStream> {
82    let public_key = match PublicKeyEd25519::from_str(&str) {
83        Ok(pk) => tokenize_slice(&pk.0),
84        Err(e) => return Err(syn::Error::new(span, format!("Invalid Ed25519 public key: {}", e))),
85    };
86
87    Ok(quote!(concordium_std::PublicKeyEd25519(#public_key)).into())
88}
89
90fn pubkey_ecdsa_worker(str: String, span: Span) -> syn::Result<TokenStream> {
91    let public_key = match PublicKeyEcdsaSecp256k1::from_str(&str) {
92        Ok(pk) => tokenize_slice(&pk.0),
93        Err(e) => return Err(syn::Error::new(span, format!("Invalid ECDSA public key: {}", e))),
94    };
95
96    Ok(quote!(concordium_std::PublicKeyEcdsaSecp256k1(#public_key)).into())
97}
98
99fn signature_ed25519_worker(str: String, span: Span) -> syn::Result<TokenStream> {
100    let signature = match SignatureEd25519::from_str(&str) {
101        Ok(sig) => tokenize_slice(&sig.0),
102        Err(e) => return Err(syn::Error::new(span, format!("Invalid Ed25519 signature: {}", e))),
103    };
104
105    Ok(quote!(concordium_std::SignatureEd25519(#signature)).into())
106}
107
108fn signature_ecdsa_worker(str: String, span: Span) -> syn::Result<TokenStream> {
109    let signature = match SignatureEcdsaSecp256k1::from_str(&str) {
110        Ok(sig) => tokenize_slice(&sig.0),
111        Err(e) => return Err(syn::Error::new(span, format!("Invalid ECDSA signature: {}", e))),
112    };
113
114    Ok(quote!(concordium_std::SignatureEcdsaSecp256k1(#signature)).into())
115}
116
117fn contract_address_worker(str: String, span: Span) -> syn::Result<TokenStream> {
118    let (index, subindex) = match ContractAddress::from_str(&str) {
119        Ok(con) => (con.index, con.subindex),
120        Err(e) => return Err(syn::Error::new(span, format!("Invalid contract address: {}", e))),
121    };
122
123    Ok(quote!(concordium_std::ContractAddress::new(#index, #subindex)).into())
124}
125
126fn module_ref_worker(str: String, span: Span) -> syn::Result<TokenStream> {
127    let module_ref = match ModuleReference::from_str(&str) {
128        Ok(mod_ref) => tokenize_slice(&mod_ref.bytes),
129        Err(e) => return Err(syn::Error::new(span, format!("Invalid module reference: {}", e))),
130    };
131
132    Ok(quote!(concordium_std::ModuleReference::new(#module_ref)).into())
133}
134
135/// Procedural macro for instantiating account addresses.
136/// Input must be a valid base58-encoding.
137#[proc_macro]
138pub fn account_address(item: TokenStream) -> TokenStream {
139    let msg = "Expected a string literal of a hex-encoded account address";
140    unwrap_or_report(get_token_res(item, msg, acc_address_worker))
141}
142
143/// Procedural macro for instantiating account addresses from an environment
144/// variable. Input must be the key of an environment variable whose value is
145/// set to a valid base58-encoding of an account address.
146#[proc_macro]
147pub fn account_address_env(item: TokenStream) -> TokenStream {
148    let msg = "Expected a string literal of a hex-encoded account address";
149    unwrap_or_report(get_token_res_env(item, msg, acc_address_worker))
150}
151
152/// Procedural macro for instantiating `PublicKeyEd25519` public keys.
153/// Input must be a (case-insensitive) hex-encoding and have a length of 64
154/// characters representing 32 bytes.
155#[proc_macro]
156pub fn public_key_ed25519(item: TokenStream) -> TokenStream {
157    let msg = "Expected a string literal of a hex-encoded ED25519 public key";
158    unwrap_or_report(get_token_res(item, msg, pubkey_ed25519_worker))
159}
160
161/// Procedural macro for instantiating `PublicKeyEd25519` public keys from
162/// an environment variable. Input must be the key of an environment variable
163/// whose value is set to a hex-encoded public key which must have a length of
164/// 64 characters representing 32 bytes.
165#[proc_macro]
166pub fn public_key_ed25519_env(item: TokenStream) -> TokenStream {
167    let msg = "Expected a string literal of a hex-encoded ED25519 public key";
168    unwrap_or_report(get_token_res_env(item, msg, pubkey_ed25519_worker))
169}
170
171/// Procedural macro for instantiating `PublicKeyEcdsaSecp256k1` public keys.
172/// Input must be a (case-insensitive) hex-encoding and have a length of 66
173/// characters representing 33 bytes.
174#[proc_macro]
175pub fn public_key_ecdsa(item: TokenStream) -> TokenStream {
176    let msg = "Expected a string literal of a hex-encoded ECDSA public key";
177    unwrap_or_report(get_token_res(item, msg, pubkey_ecdsa_worker))
178}
179
180/// Procedural macro for instantiating `PublicKeyEcdsaSecp256k1` public keys
181/// from an environment variable. Input must be the key of an environment
182/// variable whose value is set to a hex-encoded public key which must have a
183/// length of 66 characters representing 33 bytes.
184#[proc_macro]
185pub fn public_key_ecdsa_env(item: TokenStream) -> TokenStream {
186    let msg = "Expected a string literal of a hex-encoded ECDSA public key";
187    unwrap_or_report(get_token_res_env(item, msg, pubkey_ecdsa_worker))
188}
189
190/// Procedural macro for instantiating `SignatureEd25519` signatures.
191/// Input must be a (case-insensitive) hex-encoding and have a length of 128
192/// characters representing 64 bytes.
193#[proc_macro]
194pub fn signature_ed25519(item: TokenStream) -> TokenStream {
195    let msg = "Expected a string literal of a hex-encoded ED25519 signature";
196    unwrap_or_report(get_token_res(item, msg, signature_ed25519_worker))
197}
198
199/// Procedural macro for instantiating `SignatureEd25519` signatures from
200/// an environment variable. Input must be the key of an environment variable
201/// whose value is set to a hex-encoded signature which must have a length of
202/// 128 characters representing 64 bytes.
203#[proc_macro]
204pub fn signature_ed25519_env(item: TokenStream) -> TokenStream {
205    let msg = "Expected a string literal of a hex-encoded ED25519 signature";
206    unwrap_or_report(get_token_res_env(item, msg, signature_ed25519_worker))
207}
208
209/// Procedural macro for instantiating `SignatureEcdsaSecp256k1` signatures.
210/// Input must be a (case-insensitive) hex-encoding and have a length of 128
211/// characters representing 64 bytes.
212#[proc_macro]
213pub fn signature_ecdsa(item: TokenStream) -> TokenStream {
214    let msg = "Expected a string literal of a hex-encoded ECDSA signature";
215    unwrap_or_report(get_token_res(item, msg, signature_ecdsa_worker))
216}
217
218/// Procedural macro for instantiating `SignatureEcdsaSecp256k1` signatures from
219/// an environment variable. Input must be the key of an environment variable
220/// whose value is set to a hex-encoded signature which must have a length of
221/// 128 characters representing 64 bytes.
222#[proc_macro]
223pub fn signature_ecdsa_env(item: TokenStream) -> TokenStream {
224    let msg = "Expected a string literal a hex-encoded ECDSA signature";
225    unwrap_or_report(get_token_res_env(item, msg, signature_ecdsa_worker))
226}
227
228/// Procedural macro for instantiating contract addresses.
229/// Input must be of the form "<index,subindex>", where index and subindex
230/// are integers.
231#[proc_macro]
232pub fn contract_address(item: TokenStream) -> TokenStream {
233    let msg = "Expected string literal of a contract address in the form of \"<index,subindex>\"";
234    unwrap_or_report(get_token_res(item, msg, contract_address_worker))
235}
236
237/// Procedural macro for instantiating contract addresses from an environment
238/// variable. Input must be the key of an environment variable whose value is
239/// set to a contract address of the form "<index,subindex>", where index and
240/// subindex are integers
241#[proc_macro]
242pub fn contract_address_env(item: TokenStream) -> TokenStream {
243    let msg = "Expected string literal of a contract address in the form of \"<index,subindex>\"";
244    unwrap_or_report(get_token_res_env(item, msg, contract_address_worker))
245}
246
247/// Procedural macro for instantiating module references.
248/// Input must be a (case-insensitive) hex-encoding and have a length of 64
249/// characters representing 32 bytes.
250#[proc_macro]
251pub fn module_reference(item: TokenStream) -> TokenStream {
252    let msg = "Expected string literal of a hex-encoded module reference";
253    unwrap_or_report(get_token_res(item, msg, module_ref_worker))
254}
255
256/// Procedural macro for instantiating module references from an environment
257/// variable. Input must be the key of an environment variable whose value is
258/// set to a hex-encoded module reference which must have a length of 64
259/// characters representing 32 bytes.
260#[proc_macro]
261pub fn module_reference_env(item: TokenStream) -> TokenStream {
262    let msg = "Expected string literal of a hex-encoded module reference";
263    unwrap_or_report(get_token_res_env(item, msg, module_ref_worker))
264}