easy_macros_context_internal/
lib.rs1use proc_macro::TokenStream;
2use syn::{Expr, Token, punctuated::Punctuated, token::Comma};
3
4struct ContextInternalInput {
6 str: syn::LitStr,
7 _comma: Option<Token![,]>,
8 args: syn::punctuated::Punctuated<syn::Expr, Token![,]>,
9}
10
11enum ContextInternalMaybeInput {
12 Yes(ContextInternalInput),
13 No,
14}
15
16impl syn::parse::Parse for ContextInternalInput {
17 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
18 if input.is_empty() {
20 return Ok(ContextInternalInput {
21 str: syn::LitStr::new("", proc_macro2::Span::call_site()),
22 _comma: None,
23 args: syn::punctuated::Punctuated::new(),
24 });
25 }
26 let str = input.parse()?;
27 if !input.is_empty() {
28 let _comma = input.parse()?;
29 let args = input.parse_terminated(syn::Expr::parse, Token![,])?;
30 Ok(ContextInternalInput { str, _comma, args })
31 } else {
32 Ok(ContextInternalInput {
33 str,
34 _comma: None,
35 args: syn::punctuated::Punctuated::new(),
36 })
37 }
38 }
39}
40
41impl syn::parse::Parse for ContextInternalMaybeInput {
42 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
43 if input.is_empty() {
44 return Ok(ContextInternalMaybeInput::No);
45 }
46 Ok(ContextInternalMaybeInput::Yes(input.parse()?))
47 }
48}
49
50struct ContextInternalInput2 {
51 line: syn::Expr,
52 deeper: Option<ContextInternalInput>,
53}
54
55impl syn::parse::Parse for ContextInternalInput2 {
56 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
57 let line = input.parse()?;
58 if !input.is_empty() {
59 input.parse::<Token![,]>()?;
60 let deeper = input.parse()?;
61 Ok(ContextInternalInput2 {
62 line,
63 deeper: Some(deeper),
64 })
65 } else {
66 Ok(ContextInternalInput2 { line, deeper: None })
67 }
68 }
69}
70
71fn context_base(
72 mut passed_in_str: String,
73 mut passed_in_args: Punctuated<Expr, Comma>,
74 line: Expr,
75 closure: bool,
76) -> TokenStream {
77 if passed_in_str.is_empty() {
78 passed_in_str = "{}:{}".to_owned();
79 } else {
80 passed_in_str = format!("{{}}:{{}}\r\n{}", passed_in_str);
81 }
82 passed_in_args.insert(
83 0,
84 syn::parse_quote! {
85 file!()
86 },
87 );
88
89 passed_in_args.insert(1, line);
90
91 let result = if closure {
92 quote::quote! {
93 ||{format!(#passed_in_str, #passed_in_args)}
94 }
95 } else {
96 quote::quote! {
97 format!(#passed_in_str, #passed_in_args)
98 }
99 };
100
101 result.into()
104}
105
106#[proc_macro]
107pub fn context_internal(item: TokenStream) -> TokenStream {
111 let parsed = syn::parse_macro_input!(item as ContextInternalMaybeInput);
112
113 let (passed_in_str, passed_in_args) = match parsed {
114 ContextInternalMaybeInput::Yes(context_internal_input) => (
115 context_internal_input.str.value(),
116 context_internal_input.args,
117 ),
118 ContextInternalMaybeInput::No => (String::new(), syn::punctuated::Punctuated::new()),
119 };
120
121 context_base(
122 passed_in_str,
123 passed_in_args,
124 syn::parse_quote! {
125 line!()
126 },
127 false,
128 )
129}
130
131#[proc_macro]
135pub fn context_internal2(item: TokenStream) -> TokenStream {
136 let parsed = syn::parse_macro_input!(item as ContextInternalInput2);
137
138 let (passed_in_str, passed_in_args) = match parsed.deeper {
139 Some(context_internal_input) => (
140 context_internal_input.str.value(),
141 context_internal_input.args,
142 ),
143 None => (String::new(), syn::punctuated::Punctuated::new()),
144 };
145
146 context_base(passed_in_str, passed_in_args, parsed.line, true)
147}
148
149#[test]
150fn format_compiler_test() {
151 let test_str = "Str";
152 let _ = format!("file: {}:{} | {test_str} | ", file!(), line!());
153 let _ = format!("{} | file: {}:{}", test_str, file!(), line!());
154}