captains_log_helper/
lib.rs

1#![recursion_limit = "128"]
2
3
4extern crate proc_macro;
5extern crate syn;
6use proc_macro2::TokenStream;
7use quote::{quote, ToTokens};
8use syn::{
9    parse_macro_input, spanned::Spanned, token, AttributeArgs, Expr, ExprBlock, ExprClosure,
10    ItemFn, Result, ReturnType,
11};
12
13struct FormattedAttributes {
14    begin_expr: TokenStream,
15    end_expr: TokenStream,
16}
17
18impl FormattedAttributes {
19    fn get_streams(name: String) -> Self {
20        let fmt_begin = format!("+++ {} begin +++", name);
21        let fmt_end = format!("--- {} end ---", name);
22        let begin_expr = quote! {log::info!(#fmt_begin); };
23        let end_expr = quote! {log::info!(#fmt_end); };
24        FormattedAttributes { begin_expr, end_expr }
25    }
26}
27
28fn make_closure(original: &ItemFn) -> ExprClosure {
29    let body = Box::new(Expr::Block(ExprBlock {
30        attrs: Default::default(),
31        label: Default::default(),
32        block: *original.block.clone(),
33    }));
34
35    ExprClosure {
36        attrs: Default::default(),
37        asyncness: Default::default(),
38        movability: Default::default(),
39        capture: Some(token::Move { span: original.span() }),
40        or1_token: Default::default(),
41        inputs: Default::default(),
42        or2_token: Default::default(),
43        output: ReturnType::Default,
44        body,
45    }
46}
47
48fn replace_function_headers(original: ItemFn, new: &mut ItemFn) {
49    let block = new.block.clone();
50    *new = original;
51    new.block = block;
52}
53
54fn generate_function(closure: &ExprClosure, expressions: FormattedAttributes) -> Result<ItemFn> {
55    let FormattedAttributes { begin_expr, end_expr } = expressions;
56    let code = quote! {
57        fn temp() {
58            #begin_expr;
59            let _ = (#closure)();
60            #end_expr;
61        }
62    };
63
64    syn::parse2(code)
65}
66
67/// Logs the result of the function it's above.
68/// # Examples
69///
70/// ``` rust
71/// #[macro_use]
72/// extern crate captains_log;
73/// # use std::{net::*, io::{self, Write}};
74///
75/// #[logfn]
76/// fn call_isan(num: &str) -> Result<Success, Error> {
77///     if num.len() >= 10 && num.len() <= 15 {
78///         Ok(Success)
79///     } else {
80///         Err(Error)
81///     }
82/// }
83///
84/// #[logfn(err = "Error", fmt = "Failed Sending Packet: {:?}")]
85/// fn send_hi(addr: SocketAddr) -> Result<(), io::Error> {
86///     let mut stream = TcpStream::connect(addr)?;
87///     stream.write(b"Hi!")?;
88///     Ok( () )
89/// }
90///
91/// ```
92#[proc_macro_attribute]
93pub fn logfn(
94    attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
95) -> proc_macro::TokenStream {
96    let _attr = parse_macro_input!(attr as AttributeArgs);
97    let original_fn: ItemFn = parse_macro_input!(item as ItemFn);
98    let parsed_attributes = FormattedAttributes::get_streams(original_fn.sig.ident.to_string());
99    let closure = make_closure(&original_fn);
100    let mut new_fn =
101        generate_function(&closure, parsed_attributes).expect("Failed Generating Function");
102    replace_function_headers(original_fn, &mut new_fn);
103    new_fn.into_token_stream().into()
104}