use super::prelude::*;
use proc_macro_crate::{crate_name, FoundCrate};
pub fn braced_group(
brace_span: Span,
f: impl FnOnce(&mut TokenAccumulator) -> syn::Result<()>,
) -> syn::Result<proc_macro2::Group> {
delimit_token_group(Delimiter::Brace, brace_span, f)
}
pub fn delimit_token_group(
delim: Delimiter,
delim_span: Span,
f: impl FnOnce(&mut TokenAccumulator) -> syn::Result<()>,
) -> syn::Result<proc_macro2::Group> {
let mut out = TokenAccumulator::default();
f(&mut out)?;
let out = out.tokens()?;
let mut out = proc_macro2::Group::new(delim, out);
out.set_span(delim_span);
Ok(out)
}
pub struct Discard<T>(PhantomData<T>);
impl<T: Parse> Parse for Discard<T> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let _: T = input.parse()?;
Ok(Discard(PhantomData))
}
}
pub struct Concatenated<T>(pub Vec<T>);
impl<T: Parse> Parse for Concatenated<T> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut out = vec![];
while !input.is_empty() {
out.push(input.parse()?);
}
Ok(Concatenated(out))
}
}
pub fn advise_incompatibility(err_needing_advice: syn::Error) -> syn::Error {
let mut advice = Span::call_site().error(
"bad input to derive_adhoc_expand inner template expansion proc macro; might be due to incompatible derive-adhoc versions(s)"
);
advice.combine(err_needing_advice);
advice
}
pub trait MakeError {
fn error<M: Display>(&self, m: M) -> syn::Error;
}
impl<T: Spanned> MakeError for T {
fn error<M: Display>(&self, m: M) -> syn::Error {
syn::Error::new(self.span(), m)
}
}
pub type ErrorLoc = (Span, &'static str);
impl MakeError for [ErrorLoc] {
fn error<M: Display>(&self, m: M) -> syn::Error {
let mut locs = self.into_iter().cloned();
let mk = |(span, frag): (Span, _)| {
span.error(format_args!("{} ({})", &m, frag))
};
let first = locs.next().expect("at least one span needed!");
let mut build = mk(first);
for rest in locs {
build.combine(mk(rest))
}
build
}
}
pub trait ToTokensPunctComposable {
fn to_tokens_punct_composable(&self, out: &mut TokenStream);
}
impl<T, P> ToTokensPunctComposable for Punctuated<T, P>
where
T: ToTokens,
P: ToTokens + Default,
{
fn to_tokens_punct_composable(&self, out: &mut TokenStream) {
self.to_tokens(out);
if !self.empty_or_trailing() {
P::default().to_tokens(out)
}
}
}
impl<P> ToTokensPunctComposable for Option<&&P>
where
P: ToTokens,
P: Default,
{
fn to_tokens_punct_composable(&self, out: &mut TokenStream) {
if let Some(self_) = self {
self_.to_tokens(out)
} else {
P::default().to_tokens(out)
}
}
}
#[derive(Debug, Default)]
pub struct ErrorAccumulator {
bad: Option<syn::Error>,
defused: bool,
}
impl ErrorAccumulator {
pub fn handle_in<T, F>(&mut self, f: F) -> Option<T>
where
F: FnOnce() -> syn::Result<T>,
{
self.handle(f())
}
pub fn handle<T>(&mut self, result: syn::Result<T>) -> Option<T> {
match result {
Ok(y) => Some(y),
Err(e) => {
self.push(e);
None
}
}
}
pub fn push(&mut self, err: syn::Error) {
if let Some(bad) = &mut self.bad {
bad.combine(err)
} else {
self.bad = Some(err);
}
}
#[allow(dead_code)]
pub fn finish(self) -> syn::Result<()> {
self.finish_with(())
}
pub fn finish_with<T>(self, success: T) -> syn::Result<T> {
match self.into_inner() {
None => Ok(success),
Some(bad) => Err(bad),
}
}
pub fn into_inner(mut self) -> Option<syn::Error> {
self.defused = true;
self.bad.take()
}
}
impl Drop for ErrorAccumulator {
fn drop(&mut self) {
assert!(panicking() || self.defused);
}
}
pub struct IdentAny(pub syn::Ident);
impl Parse for IdentAny {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(IdentAny(Ident::parse_any(input)?))
}
}
impl Deref for IdentAny {
type Target = syn::Ident;
fn deref(&self) -> &syn::Ident {
&self.0
}
}
impl Spanned for IdentAny {
fn span(&self) -> Span {
self.0.span()
}
}
impl<T: AsRef<str> + ?Sized> PartialEq<T> for IdentAny {
fn eq(&self, rhs: &T) -> bool {
self.0.eq(rhs)
}
}
pub fn expand_macro_name() -> Result<TokenStream, syn::Error> {
let name = crate_name("derive-adhoc-macros")
.or_else(|_| crate_name("derive-adhoc"));
#[cfg(feature = "bizarre")]
let name = name.or_else(|_| crate_name("bizarre-derive-adhoc"));
match name {
Ok(FoundCrate::Itself) => Ok(quote!( crate::derive_adhoc_expand )),
Ok(FoundCrate::Name(name)) => {
let ident = Ident::new(&name, Span::call_site());
Ok(quote!( ::#ident::derive_adhoc_expand ))
}
Err(e) => Err(Span::call_site().error(
format_args!("Expected derive-adhoc or derive-adhoc-macro to be present in Cargo.toml: {}", e)
)),
}
}
macro_rules! keyword_general {
{ $kw_var:ident $from_enum:ident $Enum:ident;
$kw:ident $( $rest:tt )* } => {
keyword_general!{ $kw_var $from_enum $Enum;
@ 1 stringify!($kw), $kw, $($rest)* }
};
{ $kw_var:ident $from_enum:ident $Enum:ident;
$kw:literal: $constr:ident $( $rest:tt )* } => {
keyword_general!{ $kw_var $from_enum $Enum;
@ 1 $kw, $constr, $($rest)* }
};
{ $kw_var:ident $from_enum:ident $Enum:ident;
@ 1 $kw:expr, $constr:ident, $( $ca:tt )? } => {
keyword_general!{ $kw_var $from_enum $Enum;
@ 2 $kw, $constr, { } $( $ca )? }
};
{ $kw_var:ident $from_enum:ident $Enum:ident;
@ 1 $kw:expr, $constr:ident, { $( $bindings:tt )* } $ca:tt } => {
keyword_general!{ $kw_var $from_enum $Enum;
@ 2 $kw, $constr, { $( $bindings )* } $ca }
};
{ $kw_var:ident $from_enum:ident $Enum:ident;
@ 2 $kw:expr, $constr:ident,
{ $( $bindings:tt )* } $( $constr_args:tt )?
} => {
let _: &IdentAny = &$kw_var;
if $kw_var == $kw {
$( $bindings )*
return $from_enum($Enum::$constr $( $constr_args )*);
}
};
{ $($x:tt)* } => { compile_error!(stringify!($($x)*)) };
}