quirky_binder_codegen_macro/
lib.rs1use std::borrow::Cow;
2
3use annotate_snippets::display_list::DisplayList;
4use proc_macro2::Ident;
5use proc_macro_error::{abort_if_dirty, emit_error, proc_macro_error};
6use quirky_binder_codegen::{
7 parse_and_generate_glob_modules, parse_and_generate_module, CodegenError, ErrorEmitter,
8};
9use quirky_binder_lang::snippet::snippet_for_input_and_part;
10use quote::format_ident;
11use syn::{
12 parenthesized,
13 parse::{ParseStream, Parser},
14 token, Error, LitStr,
15};
16
17mod decorators;
18
19struct ProcMacroErrorEmitter<'a> {
20 span: &'a Ident,
21 dirty: bool,
22}
23
24impl<'a> ProcMacroErrorEmitter<'a> {
25 fn handle_codegen_result<T>(&mut self, result: Result<T, CodegenError>) -> T {
26 match result {
27 Ok(res) => {
28 assert!(!self.dirty);
29 res
30 }
31 Err(CodegenError::ErrorEmitted) => {
32 assert!(self.dirty);
33 abort_if_dirty();
34 unreachable!("should be aborted");
35 }
36 Err(CodegenError::Error(error)) => {
37 self.emit_error(error.into());
38 assert!(self.dirty);
39 abort_if_dirty();
40 unreachable!("should be aborted");
41 }
42 }
43 }
44}
45
46impl<'a> ErrorEmitter for ProcMacroErrorEmitter<'a> {
47 fn emit_error(&mut self, error: Cow<str>) -> CodegenError {
48 emit_error!(self.span, "{}", error);
49 self.dirty = true;
50 CodegenError::ErrorEmitted
51 }
52
53 fn emit_quirky_binder_error(&mut self, src: &str, part: &str, error: Cow<str>) {
54 emit_error!(
55 self.span,
56 "{}",
57 DisplayList::from(snippet_for_input_and_part(&error, src, part))
58 );
59 self.dirty = true;
60 }
61
62 fn error(&mut self) -> Result<(), quirky_binder_codegen::CodegenError> {
63 if self.dirty {
64 Err(CodegenError::ErrorEmitted)
65 } else {
66 Ok(())
67 }
68 }
69}
70
71#[derive(PartialEq, Eq, Clone, Debug)]
72enum Definition {
73 Inline {
74 qb: String,
75 },
76 IncludeGlob {
77 src: String,
78 pattern: String,
79 test: bool,
80 },
81}
82
83const INLINE_DEFINITION: &str = "inline";
84const INCLUDE_GLOB_DEFINITION: &str = "include_glob";
85const INCLUDE_GLOB_TEST_DEFINITION: &str = "include_glob_test";
86
87fn parse_def(input: ParseStream) -> Result<(Definition, Ident), Error> {
88 let def_type: Ident = input.parse()?;
89 let def = match def_type.to_string().as_str() {
90 INLINE_DEFINITION => {
91 let content;
92 parenthesized!(content in input);
93 let qb: LitStr = content.parse()?;
94 Definition::Inline { qb: qb.value() }
95 }
96 INCLUDE_GLOB_DEFINITION => {
97 let content;
98 parenthesized!(content in input);
99 Definition::parse_include_glob(&content, false)?
100 }
101 INCLUDE_GLOB_TEST_DEFINITION => {
102 let content;
103 parenthesized!(content in input);
104 Definition::parse_include_glob(&content, true)?
105 }
106 _ => {
107 return Err(Error::new(
108 def_type.span(),
109 format!(
110 "expected [{}]",
111 [INLINE_DEFINITION, INCLUDE_GLOB_DEFINITION].join(", ")
112 ),
113 ));
114 }
115 };
116 Ok((def, def_type))
117}
118
119impl Definition {
120 fn parse_include_glob(content: ParseStream, test: bool) -> Result<Definition, Error> {
121 let src: LitStr = content.parse()?;
122 content.parse::<token::Comma>()?;
123 let pattern: LitStr = content.parse()?;
124 Ok(Definition::IncludeGlob {
125 src: src.value(),
126 pattern: pattern.value(),
127 test,
128 })
129 }
130}
131
132fn quirky_binder_1(
133 input: proc_macro::TokenStream,
134 quirky_binder_crate: &Ident,
135) -> proc_macro::TokenStream {
136 let (def, span) = match Parser::parse(parse_def, input) {
137 Ok(res) => res,
138 Err(err) => {
139 abort_if_dirty();
140 return err.into_compile_error().into();
141 }
142 };
143
144 let mut error_emitter = ProcMacroErrorEmitter {
145 span: &span,
146 dirty: false,
147 };
148
149 match def {
150 Definition::Inline { qb } => {
151 let result =
152 parse_and_generate_module(&qb, None, quirky_binder_crate, &mut error_emitter);
153 error_emitter.handle_codegen_result(result).into()
154 }
155 Definition::IncludeGlob { src, pattern, test } => {
156 let result = parse_and_generate_glob_modules(
157 &src,
158 &pattern,
159 quirky_binder_crate,
160 test,
161 &mut error_emitter,
162 );
163 error_emitter.handle_codegen_result(result).into()
164 }
165 }
166}
167
168#[proc_macro]
169#[proc_macro_error]
170pub fn quirky_binder(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
171 quirky_binder_1(input, &format_ident!("quirky_binder"))
172}
173
174#[proc_macro]
175#[proc_macro_error]
176pub fn quirky_binder_internal(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
177 quirky_binder_1(input, &format_ident!("crate"))
178}
179
180#[proc_macro]
181pub fn tracking_allocator_static(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
182 decorators::tracking_allocator_static(input)
183}
184
185#[proc_macro_attribute]
186pub fn tracking_allocator_main(
187 attr: proc_macro::TokenStream,
188 item: proc_macro::TokenStream,
189) -> proc_macro::TokenStream {
190 decorators::tracking_allocator_main(attr, item)
191}