use super::prelude::*;
use proc_macro_crate::{crate_name, FoundCrate};
#[cfg(not(derive_deftly_dprint))]
#[macro_use]
pub mod dprint {
macro_rules! dprintln { { $( $x:tt )* } => {} }
macro_rules! dprint_block { { $( $x:tt )* } => {} }
}
#[cfg(derive_deftly_dprint)]
#[macro_use]
pub mod dprint {
pub fn wanted() -> bool {
const VAR: &str = "DERIVE_DEFTLY_DPRINT";
match std::env::var_os(VAR) {
None => false,
Some(y) if y == "0" => false,
Some(y) if y == "1" => true,
other => panic!("bad value for {}: {:?}", VAR, other),
}
}
macro_rules! dprintln { { $( $x:tt )* } => { {
if dprint::wanted() {
eprintln!( $($x)* )
}
} } }
macro_rules! dprint_block { {
[ $($value:expr),* $(,)? ], $f:literal $( $x:tt )*
} => { {
dprintln!(concat!("---------- ", $f, " start ----------") $($x)*);
dprint_block!(@ $( $value, )*);
dprintln!(concat!("---------- ", $f, " end ----------") $($x)*);
} }; {
@ $value:expr,
} => { {
dprintln!("{}", $value);
} }; {
@ $value:expr, $($more:tt)+
} => { {
dprint_block!(@ $value,);
dprintln!("----------");
dprint_block!(@ $($more)+);
} }; {
$value:expr, $f:literal $( $x:tt )*
} => { {
dprint_block!([$value], $f $($x)*);
} } }
}
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)
}
#[allow(dead_code)] 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))
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)] 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))
}
}
impl<T: ToTokens> ToTokens for Concatenated<T> {
fn to_tokens(&self, out: &mut TokenStream) {
for item in &self.0 {
item.to_tokens(out);
}
}
}
pub fn typepath_add_missing_argument_colons(
type_path: &mut syn::TypePath,
te_span: Span,
) {
for seg in &mut type_path.path.segments {
match &mut seg.arguments {
syn::PathArguments::None => {}
syn::PathArguments::Parenthesized(_) => {}
syn::PathArguments::AngleBracketed(args) => {
args.colon2_token.get_or_insert_with(|| {
Token
});
}
}
}
}
pub fn dummy_path() -> syn::Path {
syn::Path {
leading_colon: None,
segments: Punctuated::default(),
}
}
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<'s> = (Span, &'s 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 fn spans_join(spans: impl IntoIterator<Item = Span>) -> Option<Span> {
spans.into_iter().reduce(|a, b| a.join(b).unwrap_or(b))
}
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()
}
pub fn examine(&self) -> Option<&syn::Error> {
self.bad.as_ref()
}
}
impl Drop for ErrorAccumulator {
fn drop(&mut self) {
assert!(panicking() || self.defused);
}
}
#[derive(Debug, Clone)]
pub struct MacroExport(Span);
impl Spanned for MacroExport {
fn span(&self) -> Span {
self.0
}
}
impl MacroExport {
pub fn parse_option(input: ParseStream) -> syn::Result<Option<Self>> {
let span = if let Some(vis) = input.parse::<Option<Token![pub]>>()? {
return Err(vis.error(
"You must now write `define_derive_deftly! { export Template: ... }`, not `puib Template:`, since derive-deftly version 0.14.0"
));
} else if let Some(export) = (|| {
use syn::parse::discouraged::Speculative;
input.peek(syn::Ident).then(|| ())?;
let forked = input.fork();
let ident: syn::Ident = forked.parse().expect("it *was*");
(ident == "export").then(|| ())?;
input.advance_to(&forked);
Some(ident)
})() {
Some(export.span())
} else {
None
};
Ok(span.map(MacroExport))
}
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Grouping {
Ungrouped,
Invisible,
Parens,
}
impl Grouping {
pub fn surround(&self, ts: impl ToTokens) -> TokenStream {
let ts = ts.to_token_stream();
match self {
Grouping::Ungrouped => ts,
Grouping::Invisible => {
proc_macro2::Group::new(Delimiter::None, ts).to_token_stream()
}
Grouping::Parens => {
proc_macro2::Group::new(Delimiter::Parenthesis, ts)
.to_token_stream()
}
}
}
}
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 ToTokens for IdentAny {
fn to_tokens(&self, out: &mut TokenStream) {
self.0.to_tokens(out)
}
}
impl<T: AsRef<str> + ?Sized> PartialEq<T> for IdentAny {
fn eq(&self, rhs: &T) -> bool {
self.0.eq(rhs)
}
}
pub struct TokenOutputTrimmer<'t, 'o> {
preamble: Option<&'t dyn ToTokens>,
sep: &'t dyn ToTokens,
sep_count: usize,
out: &'o mut TokenStream,
}
impl<'t, 'o> TokenOutputTrimmer<'t, 'o> {
pub fn new(
out: &'o mut TokenStream,
preamble: &'t dyn ToTokens,
sep: &'t dyn ToTokens,
) -> Self {
TokenOutputTrimmer {
preamble: Some(preamble),
sep,
sep_count: 0,
out,
}
}
pub fn push_sep(&mut self) {
self.sep_count += 1;
}
fn reify(&mut self) {
if let Some(preamble) = self.preamble.take() {
preamble.to_tokens(&mut self.out);
}
for _ in 0..mem::take(&mut self.sep_count) {
self.sep.to_tokens(&mut self.out);
}
}
pub fn push_reified(&mut self, t: impl ToTokens) {
self.reify();
t.to_tokens(&mut self.out);
}
pub fn did_preamble(self) -> Option<()> {
if self.preamble.is_some() {
None
} else {
Some(())
}
}
}
#[derive(Debug, Clone)]
pub struct TemplateName(syn::Ident);
impl TemplateName {
pub fn macro_name(&self) -> syn::Ident {
format_ident!("derive_deftly_template_{}", &self.0)
}
}
impl Parse for TemplateName {
fn parse(input: ParseStream) -> syn::Result<Self> {
let ident: syn::Ident = input.parse()?;
ident.try_into()
}
}
impl TryFrom<syn::Ident> for TemplateName {
type Error = syn::Error;
fn try_from(ident: syn::Ident) -> syn::Result<TemplateName> {
let s = ident.to_string();
match s.chars().find(|&c| c != '_') {
None => {
Err("template name cannot consist entirely of underscores")
}
Some(c) => {
if c.is_lowercase() {
Err(
"template name may not start with a lowercase letter (after any underscores)"
)
} else {
Ok(())
}
}
}
.map_err(|emsg| ident.error(emsg))?;
Ok(TemplateName(ident))
}
}
impl ToTokens for TemplateName {
fn to_tokens(&self, out: &mut TokenStream) {
self.0.to_tokens(out)
}
}
impl Display for TemplateName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl quote::IdentFragment for TemplateName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
quote::IdentFragment::fmt(&self.0, f)
}
fn span(&self) -> Option<Span> {
quote::IdentFragment::span(&self.0)
}
}
pub fn engine_macro_name() -> Result<TokenStream, syn::Error> {
let name = crate_name("derive-deftly-macros")
.or_else(|_| crate_name("derive-deftly"));
#[cfg(feature = "bizarre")]
let name = name.or_else(|_| crate_name("bizarre-derive-deftly"));
match name {
Ok(FoundCrate::Itself) => Ok(quote!( crate::derive_deftly_engine )),
Ok(FoundCrate::Name(name)) => {
let ident = Ident::new(&name, Span::call_site());
Ok(quote!( ::#ident::derive_deftly_engine ))
}
Err(e) => Err(Span::call_site().error(
format_args!("Expected derive-deftly or derive-deftly-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)*)) };
}