macron_input/
lib.rs

1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2
3//! **See more useful macros [here](https://docs.rs/macron)!**
4
5use proc_macro::TokenStream;
6use proc_macro2::TokenStream as TokenStream2;
7use quote::quote;
8
9/// Reads user inputs from the console
10#[proc_macro]
11pub fn input(input: TokenStream) -> TokenStream {
12    let Message { msg } = syn::parse_macro_input!(input as Message);
13    
14    // parsing message:
15    let print_msg = msg
16        .map(|Format { expr, args }| {
17            let msg = match expr {
18                // literal string:
19                syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(fmt), .. }) => {
20                    // inputln!("a format {}..", "text")
21                    if args.is_some() {
22                        quote! { &::std::format!(#fmt, #args) }
23                    }
24                    // inputln!("a format {text}..")
25                    else if fmt.value().contains(&['{', '}'][..])  {
26                        quote! { &::std::format!(#fmt) }
27                    }
28                    // inputln!("a simple text..")
29                    else {
30                        quote! { #fmt }
31                    }
32                },
33
34                // NOTE: chill out, this panic will never work.
35                _ => panic!()
36            };
37
38            quote! {
39                use ::std::io::Write;
40
41                ::std::io::stdout().write_all(#msg.as_bytes()).unwrap();
42                ::std::io::stdout().flush().unwrap();
43            }
44        })
45        .unwrap_or(quote! {});
46
47    // reading user input:
48    quote! {{
49        #print_msg
50
51        use ::std::io::BufRead;
52        
53        ::std::io::stdin().lock().lines()
54    }}
55    .into()
56}
57
58// The input message
59struct Message {
60    pub msg: Option<Format>
61}
62
63impl syn::parse::Parse for Message {
64    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
65        Ok(Self { msg: input.parse().ok() })
66    }
67}
68
69// The string formatter
70struct Format {
71    pub expr: syn::Expr,
72    pub args: Option<TokenStream2>,
73}
74
75impl syn::parse::Parse for Format {
76    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
77        // parse expression:
78        let expr = input.parse()?;
79
80        // check expr to literal string:
81        if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(_), .. }) = &expr {}
82        else { return Err(syn::Error::new_spanned(expr, "Expected literal string")) }
83
84        // parse arguments:
85        let args = if input.peek(syn::token::Comma) {
86            Some(input.parse()?)
87        } else {
88            None
89        };
90        
91        Ok(Self { expr, args })
92    }
93}