include_crypt_bytes_macro/
lib.rs1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use std::fs;
4use syn::{
5 fold::{self, Fold},
6 parse::{Parse, ParseStream, Result},
7 parse_macro_input, parse_quote,
8 token::Comma,
9 FnArg, ItemFn, LitStr, Pat, PathSegment, ReturnType, Signature, Type, Visibility, Expr
10};
11
12struct Args {
13 get_type: bool,
14 captured_type: Option<Type>,
15}
16
17impl Parse for Args {
18 fn parse(_input: ParseStream) -> Result<Self> {
19 Ok(Args {
20 get_type: false,
21 captured_type: None,
22 })
23 }
24}
25
26impl Fold for Args {
27 fn fold_signature(&mut self, i: Signature) -> Signature {
28 let mut i = fold::fold_signature(self, i);
29
30 if let Some(captured) = self.captured_type.take() {
31 if let ReturnType::Type(_, ty) = &mut i.output {
32 *ty = Box::new(captured)
33 }
34 }
35
36 i
37 }
38
39 fn fold_path_segment(&mut self, i: PathSegment) -> PathSegment {
40 if i.ident == "ImapResult" {
41 self.get_type = true;
42 }
43
44 fold::fold_path_segment(self, i)
45 }
46
47 fn fold_type(&mut self, i: Type) -> Type {
48 if self.get_type {
49 self.captured_type = Some(i.clone());
50 self.get_type = false;
51 }
52
53 fold::fold_type(self, i)
54 }
55}
56
57#[proc_macro_attribute]
58pub fn retry_imap(metadata: TokenStream, input: TokenStream) -> TokenStream {
59 let mut input_fn = parse_macro_input!(input as ItemFn);
61 let mut args = parse_macro_input!(metadata as Args);
62
63 let input_fn_with_underscore = {
64 let mut with_undscor = input_fn.clone();
65 with_undscor.sig.ident = format_ident!("__{}", &with_undscor.sig.ident);
66 with_undscor.vis = Visibility::Inherited;
67 with_undscor
68 };
69
70 input_fn.sig = args.fold_signature(input_fn.sig.clone());
71 let mut input_args = vec![];
72
73 for arg in input_fn.sig.inputs.iter() {
74 match arg {
75 FnArg::Receiver(_) => (),
76 FnArg::Typed(pt) => {
77 if let Pat::Ident(pi) = pt.pat.as_ref() {
78 input_args.push(pi.ident.clone());
79 }
80 }
81 }
82 }
83
84 let input_args_ref = input_args.iter();
85 let fn_sig_undrscor = &input_fn_with_underscore.sig.ident;
86
87 input_fn.block = parse_quote!(
88 {
89 let mut backoff = ExpBackoff::new(&self.username);
90
91 loop {
92 let er = match self. #fn_sig_undrscor (#(#input_args_ref , )*).await {
93 Err(e) => e,
94 Ok(r) => return r,
95 };
96 log::error!("Failed IMAP: {:?}", er);
97 self.refresh();
98 backoff.wait().await;
99 }
100 }
101 );
102
103 TokenStream::from(quote!(
104 #input_fn_with_underscore
105
106 #input_fn
107 ))
108}
109
110struct CryptArgs {
111 path: String,
112 password_exp: Expr,
113 password_env_var: String,
114}
115
116impl Parse for CryptArgs {
117 fn parse(input: ParseStream) -> Result<Self> {
118 let path = input.parse::<LitStr>()?.value();
119 input.parse::<Comma>()?;
120 let password_ident: Expr = input.parse()?;
121 input.parse::<Comma>()?;
122 let password_env_var = input.parse::<LitStr>()?.value();
123
124 Ok(CryptArgs {
125 path,
126 password_exp: password_ident,
127 password_env_var,
128 })
129 }
130}
131
132#[proc_macro]
133pub fn include_bytes_crypt(input: TokenStream) -> TokenStream {
134 let args = parse_macro_input!(input as CryptArgs);
135
136 let bytes = fs::read(&args.path)
137 .unwrap_or_else(|_| panic!("Could not read file at path {}", &args.path));
138 let password = std::env::var(&args.password_env_var)
139 .unwrap_or_else(|_| panic!("Could not get password from env {}", &args.password_env_var));
140 let (ciphertext, nonce, salt) =
141 include_crypt_bytes_cipher::encrypt_bytes(&bytes, password.as_bytes()).expect("Could not encrypt message");
142 let password_exp = &args.password_exp;
143
144 let ciphertext_length = ciphertext.len();
145 let nonce_length = nonce.len();
146 let salt_length = salt.len();
147
148 let q = quote!(
149 {
150 const ciphertext: [u8; #ciphertext_length] = [ #(#ciphertext , )* ] ;
151 const nonce: [u8; #nonce_length] = [ #(#nonce , )* ] ;
152 const salt: [u8; #salt_length] = [ #(#salt , )* ] ;
153
154 include_crypt_bytes::decrypt_bytes(&ciphertext, #password_exp, &nonce, &salt)
155 }
156 );
157
158 TokenStream::from(q)
159}
160
161struct ObfuscateArgs {
162 path: String,
163}
164
165impl Parse for ObfuscateArgs {
166 fn parse(input: ParseStream) -> Result<Self> {
167 let path = input.parse::<LitStr>()?.value();
168
169 Ok(ObfuscateArgs {
170 path,
171 })
172 }
173}
174
175#[proc_macro]
176pub fn include_bytes_obfuscate(input: TokenStream) -> TokenStream {
177 use rand::rngs::OsRng;
178 use rand::RngCore;
179
180 let args = parse_macro_input!(input as ObfuscateArgs);
181
182 let bytes = fs::read(&args.path)
183 .unwrap_or_else(|_| panic!("Could not read file at path {}", &args.path));
184
185 const PASSWORD_LENGTH: usize = 32;
186 let mut password = [0u8; PASSWORD_LENGTH];
187 OsRng.fill_bytes(&mut password);
188
189 let (ciphertext, nonce, salt) =
190 include_crypt_bytes_cipher::encrypt_bytes(&bytes, &password).expect("Could not encrypt message");
191
192 let ciphertext_length = ciphertext.len();
193 let nonce_length = nonce.len();
194 let salt_length = salt.len();
195
196 let q = quote!(
197 {
198 const ciphertext: [u8; #ciphertext_length] = [ #(#ciphertext , )* ] ;
199 const nonce: [u8; #nonce_length] = [ #(#nonce , )* ] ;
200 const salt: [u8; #salt_length] = [ #(#salt , )* ] ;
201 const password: [u8; #PASSWORD_LENGTH] = [ #(#password , )* ] ;
202
203 include_crypt_bytes::decrypt_bytes(&ciphertext, &password, &nonce, &salt)
204 }
205 );
206
207 TokenStream::from(q)
208}