loggy_macros/
lib.rs

1//! Procedural macros for the loggy framework.
2//!
3//! If/when Rust allows placing procedural macros inside a library crate, this crate should be merged into the overall
4//! loggy crate.
5
6#![deny(missing_docs)]
7
8extern crate syn;
9
10use proc_macro::TokenStream;
11use quote::quote;
12use syn::parse::Parse;
13use syn::parse::ParseStream;
14use syn::parse_macro_input;
15use syn::parse_quote;
16use syn::ItemFn;
17use syn::LitStr;
18use syn::Result;
19use syn::Stmt;
20
21/// How to parse a scope name argument.
22struct ScopeName {
23    string: LitStr,
24}
25
26impl Parse for ScopeName {
27    fn parse(stream: ParseStream) -> Result<Self> {
28        Ok(Self {
29            string: stream.parse()?,
30        })
31    }
32}
33
34/// Mark a function as a scope.
35///
36/// To use this, prefix the test with `#[loggy::scope]` or `#[loggy::scope("name")]`. All log messages generated in the
37/// code invoked by the function will be prefixed by the scope name (by default, the function name) instead of the
38/// default (module name).
39///
40/// # Panics
41///
42/// If the code invoked by the function generated any error messages.
43#[proc_macro_attribute]
44pub fn scope(attributes: TokenStream, stream: TokenStream) -> TokenStream {
45    let mut input = parse_macro_input!(stream as ItemFn);
46    let name = if attributes.is_empty() {
47        input.sig.ident.to_string()
48    } else {
49        parse_macro_input!(attributes as ScopeName).string.value()
50    };
51    let prefix: Stmt = parse_quote! { let _loggy_scope = loggy::Scope::new(#name); };
52    input.block.stmts.insert(0, prefix);
53    let output = quote! { #input };
54    output.into()
55}