include_crypt_bytes_macro/
lib.rs

1use 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    // returing a simple TokenStream for Struct
60    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}