macron_inputln/
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 input line from the console
10#[proc_macro]
11pub fn inputln(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 line:
48    quote! {{
49        #print_msg
50        
51        let mut buffer = String::new();
52        ::std::io::stdin().read_line(&mut buffer).unwrap();
53
54        buffer.trim().to_owned()
55    }}
56    .into()
57}
58
59// The input message
60struct Message {
61    pub msg: Option<Format>
62}
63
64impl syn::parse::Parse for Message {
65    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
66        Ok(Self { msg: input.parse().ok() })
67    }
68}
69
70// The string formatter
71struct Format {
72    pub expr: syn::Expr,
73    pub args: Option<TokenStream2>,
74}
75
76impl syn::parse::Parse for Format {
77    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
78        // parse expression:
79        let expr = input.parse()?;
80
81        // check expr to literal string:
82        if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(_), .. }) = &expr {}
83        else { return Err(syn::Error::new_spanned(expr, "Expected literal string")) }
84
85        // parse arguments:
86        let args = if input.peek(syn::token::Comma) {
87            Some(input.parse()?)
88        } else {
89            None
90        };
91        
92        Ok(Self { expr, args })
93    }
94}