use std::borrow::Cow;
use annotate_snippets::display_list::DisplayList;
use proc_macro2::Ident;
use proc_macro_error::{abort_if_dirty, emit_error, proc_macro_error};
use quirky_binder_codegen::{
parse_and_generate_glob_modules, parse_and_generate_module, CodegenError, ErrorEmitter,
};
use quirky_binder_lang::snippet::snippet_for_input_and_part;
use quote::format_ident;
use syn::{
parenthesized,
parse::{ParseStream, Parser},
token, Error, LitStr,
};
mod decorators;
struct ProcMacroErrorEmitter<'a> {
span: &'a Ident,
dirty: bool,
}
impl<'a> ProcMacroErrorEmitter<'a> {
fn handle_codegen_result<T>(&mut self, result: Result<T, CodegenError>) -> T {
match result {
Ok(res) => {
assert!(!self.dirty);
res
}
Err(CodegenError::ErrorEmitted) => {
assert!(self.dirty);
abort_if_dirty();
unreachable!("should be aborted");
}
Err(CodegenError::Error(error)) => {
self.emit_error(error.into());
assert!(self.dirty);
abort_if_dirty();
unreachable!("should be aborted");
}
}
}
}
impl<'a> ErrorEmitter for ProcMacroErrorEmitter<'a> {
fn emit_error(&mut self, error: Cow<str>) -> CodegenError {
emit_error!(self.span, "{}", error);
self.dirty = true;
CodegenError::ErrorEmitted
}
fn emit_quirky_binder_error(&mut self, src: &str, part: &str, error: Cow<str>) {
emit_error!(
self.span,
"{}",
DisplayList::from(snippet_for_input_and_part(&error, src, part))
);
self.dirty = true;
}
fn error(&mut self) -> Result<(), quirky_binder_codegen::CodegenError> {
if self.dirty {
Err(CodegenError::ErrorEmitted)
} else {
Ok(())
}
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
enum Definition {
Inline {
qb: String,
},
IncludeGlob {
src: String,
pattern: String,
test: bool,
},
}
const INLINE_DEFINITION: &str = "inline";
const INCLUDE_GLOB_DEFINITION: &str = "include_glob";
const INCLUDE_GLOB_TEST_DEFINITION: &str = "include_glob_test";
fn parse_def(input: ParseStream) -> Result<(Definition, Ident), Error> {
let def_type: Ident = input.parse()?;
let def = match def_type.to_string().as_str() {
INLINE_DEFINITION => {
let content;
parenthesized!(content in input);
let qb: LitStr = content.parse()?;
Definition::Inline { qb: qb.value() }
}
INCLUDE_GLOB_DEFINITION => {
let content;
parenthesized!(content in input);
Definition::parse_include_glob(&content, false)?
}
INCLUDE_GLOB_TEST_DEFINITION => {
let content;
parenthesized!(content in input);
Definition::parse_include_glob(&content, true)?
}
_ => {
return Err(Error::new(
def_type.span(),
format!(
"expected [{}]",
[INLINE_DEFINITION, INCLUDE_GLOB_DEFINITION].join(", ")
),
));
}
};
Ok((def, def_type))
}
impl Definition {
fn parse_include_glob(content: ParseStream, test: bool) -> Result<Definition, Error> {
let src: LitStr = content.parse()?;
content.parse::<token::Comma>()?;
let pattern: LitStr = content.parse()?;
Ok(Definition::IncludeGlob {
src: src.value(),
pattern: pattern.value(),
test,
})
}
}
fn quirky_binder_1(
input: proc_macro::TokenStream,
quirky_binder_crate: &Ident,
) -> proc_macro::TokenStream {
let (def, span) = match Parser::parse(parse_def, input) {
Ok(res) => res,
Err(err) => {
abort_if_dirty();
return err.into_compile_error().into();
}
};
let mut error_emitter = ProcMacroErrorEmitter {
span: &span,
dirty: false,
};
match def {
Definition::Inline { qb } => {
let result =
parse_and_generate_module(&qb, None, quirky_binder_crate, &mut error_emitter);
error_emitter.handle_codegen_result(result).into()
}
Definition::IncludeGlob { src, pattern, test } => {
let result = parse_and_generate_glob_modules(
&src,
&pattern,
quirky_binder_crate,
test,
&mut error_emitter,
);
error_emitter.handle_codegen_result(result).into()
}
}
}
#[proc_macro]
#[proc_macro_error]
pub fn quirky_binder(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
quirky_binder_1(input, &format_ident!("quirky_binder"))
}
#[proc_macro]
#[proc_macro_error]
pub fn quirky_binder_internal(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
quirky_binder_1(input, &format_ident!("crate"))
}
#[proc_macro]
pub fn tracking_allocator_static(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
decorators::tracking_allocator_static(input)
}
#[proc_macro_attribute]
pub fn tracking_allocator_main(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
decorators::tracking_allocator_main(attr, item)
}