use std::collections::BTreeSet;
pub(crate) use self::context_module::ContextModule;
pub(crate) use self::context_selector::ContextSelector;
pub(crate) use self::display::{Display, DisplayMatchArm};
pub(crate) use self::error::{Error, ErrorProvideMatchArm, ErrorSourceMatchArm};
pub(crate) use self::error_compat::{ErrorCompat, ErrorCompatBacktraceMatchArm};
pub(crate) struct StaticIdent(&'static str);
impl quote::ToTokens for StaticIdent {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
proc_macro2::Ident::new(self.0, proc_macro2::Span::call_site()).to_tokens(tokens)
}
}
struct AllFieldNames<'a>(&'a crate::FieldContainer);
impl<'a> AllFieldNames<'a> {
fn field_names(&self) -> BTreeSet<&'a proc_macro2::Ident> {
let user_fields = self.0.selector_kind.user_fields();
let backtrace_field = self.0.backtrace_field.as_ref();
let implicit_fields = &self.0.implicit_fields;
let message_field = self.0.selector_kind.message_field();
let source_field = self.0.selector_kind.source_field();
user_fields
.iter()
.chain(backtrace_field)
.chain(implicit_fields)
.chain(message_field)
.map(crate::Field::name)
.chain(source_field.map(crate::SourceField::name))
.collect()
}
}
pub mod context_module {
use crate::ModuleName;
use heck::ToSnakeCase;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::Ident;
#[derive(Copy, Clone)]
pub(crate) struct ContextModule<'a, T> {
pub container_name: &'a Ident,
pub module_name: &'a ModuleName,
pub visibility: Option<&'a dyn ToTokens>,
pub body: &'a T,
}
impl<'a, T> ToTokens for ContextModule<'a, T>
where
T: ToTokens,
{
fn to_tokens(&self, stream: &mut TokenStream) {
let module_name = match self.module_name {
ModuleName::Default => {
let name_str = self.container_name.to_string().to_snake_case();
syn::Ident::new(&name_str, self.container_name.span())
}
ModuleName::Custom(name) => name.clone(),
};
let visibility = self.visibility;
let body = self.body;
let module_tokens = quote! {
#visibility mod #module_name {
use super::*;
#body
}
};
stream.extend(module_tokens);
}
}
}
pub mod context_selector {
use crate::{ContextSelectorKind, Field, SuffixKind};
use proc_macro2::TokenStream;
use quote::{format_ident, quote, IdentFragment, ToTokens};
const DEFAULT_SUFFIX: &str = "Snafu";
#[derive(Copy, Clone)]
pub(crate) struct ContextSelector<'a> {
pub backtrace_field: Option<&'a Field>,
pub implicit_fields: &'a [Field],
pub crate_root: &'a dyn ToTokens,
pub error_constructor_name: &'a dyn ToTokens,
pub original_generics_without_defaults: &'a [TokenStream],
pub parameterized_error_name: &'a dyn ToTokens,
pub selector_doc_string: &'a str,
pub selector_kind: &'a ContextSelectorKind,
pub selector_name: &'a proc_macro2::Ident,
pub user_fields: &'a [Field],
pub visibility: Option<&'a dyn ToTokens>,
pub where_clauses: &'a [TokenStream],
pub default_suffix: &'a SuffixKind,
}
impl ToTokens for ContextSelector<'_> {
fn to_tokens(&self, stream: &mut TokenStream) {
use self::ContextSelectorKind::*;
let context_selector = match self.selector_kind {
Context { source_field, .. } => {
let context_selector_type = self.generate_type();
let context_selector_impl = match source_field {
Some(_) => None,
None => Some(self.generate_leaf()),
};
let context_selector_into_error_impl =
self.generate_into_error(source_field.as_ref());
quote! {
#context_selector_type
#context_selector_impl
#context_selector_into_error_impl
}
}
Whatever {
source_field,
message_field,
} => self.generate_whatever(source_field.as_ref(), message_field),
NoContext { source_field } => self.generate_from_source(source_field),
};
stream.extend(context_selector)
}
}
impl ContextSelector<'_> {
fn user_field_generics(&self) -> Vec<proc_macro2::Ident> {
(0..self.user_fields.len())
.map(|i| format_ident!("__T{}", i))
.collect()
}
fn user_field_names(&self) -> Vec<&syn::Ident> {
self.user_fields
.iter()
.map(|Field { name, .. }| name)
.collect()
}
fn parameterized_selector_name(&self) -> TokenStream {
let selector_name = self.selector_name.to_string();
let selector_name = selector_name.trim_end_matches("Error");
let suffix: &dyn IdentFragment = match self.selector_kind {
ContextSelectorKind::Context { suffix, .. } => {
match suffix.resolve_with_default(self.default_suffix) {
SuffixKind::Some(s) => s,
SuffixKind::None => &"",
SuffixKind::Default => &DEFAULT_SUFFIX,
}
}
_ => &DEFAULT_SUFFIX,
};
let selector_name = format_ident!(
"{}{}",
selector_name,
suffix,
span = self.selector_name.span()
);
let user_generics = self.user_field_generics();
quote! { #selector_name<#(#user_generics,)*> }
}
fn extended_where_clauses(&self) -> Vec<TokenStream> {
let user_fields = self.user_fields;
let user_field_generics = self.user_field_generics();
let where_clauses = self.where_clauses;
let target_types = user_fields
.iter()
.map(|Field { ty, .. }| quote! { ::core::convert::Into<#ty>});
user_field_generics
.into_iter()
.zip(target_types)
.map(|(gen, bound)| quote! { #gen: #bound })
.chain(where_clauses.iter().cloned())
.collect()
}
fn transfer_user_fields(&self) -> Vec<TokenStream> {
self.user_field_names()
.into_iter()
.map(|name| {
quote! { #name: ::core::convert::Into::into(self.#name) }
})
.collect()
}
fn construct_implicit_fields(&self) -> TokenStream {
let crate_root = self.crate_root;
let expression = quote! {
#crate_root::GenerateImplicitData::generate()
};
self.construct_implicit_fields_with_expression(expression)
}
fn construct_implicit_fields_with_source(&self) -> TokenStream {
let crate_root = self.crate_root;
let expression = quote! { {
use #crate_root::AsErrorSource;
let error = error.as_error_source();
#crate_root::GenerateImplicitData::generate_with_source(error)
} };
self.construct_implicit_fields_with_expression(expression)
}
fn construct_implicit_fields_with_expression(
&self,
expression: TokenStream,
) -> TokenStream {
self.implicit_fields
.iter()
.chain(self.backtrace_field)
.map(|field| {
let name = &field.name;
quote! { #name: #expression, }
})
.collect()
}
fn generate_type(self) -> TokenStream {
let visibility = self.visibility;
let parameterized_selector_name = self.parameterized_selector_name();
let user_field_generics = self.user_field_generics();
let user_field_names = self.user_field_names();
let selector_doc_string = self.selector_doc_string;
let body = if user_field_names.is_empty() {
quote! { ; }
} else {
quote! {
{
#(
#[allow(missing_docs)]
#visibility #user_field_names: #user_field_generics
),*
}
}
};
quote! {
#[derive(Debug, Copy, Clone)]
#[doc = #selector_doc_string]
#visibility struct #parameterized_selector_name #body
}
}
fn generate_leaf(self) -> TokenStream {
let error_constructor_name = self.error_constructor_name;
let original_generics_without_defaults = self.original_generics_without_defaults;
let parameterized_error_name = self.parameterized_error_name;
let parameterized_selector_name = self.parameterized_selector_name();
let user_field_generics = self.user_field_generics();
let visibility = self.visibility;
let extended_where_clauses = self.extended_where_clauses();
let transfer_user_fields = self.transfer_user_fields();
let construct_implicit_fields = self.construct_implicit_fields();
let track_caller = track_caller();
quote! {
impl<#(#user_field_generics,)*> #parameterized_selector_name {
#[doc = "Consume the selector and return the associated error"]
#[must_use]
#track_caller
#visibility fn build<#(#original_generics_without_defaults,)*>(self) -> #parameterized_error_name
where
#(#extended_where_clauses),*
{
#error_constructor_name {
#construct_implicit_fields
#(#transfer_user_fields,)*
}
}
#[doc = "Consume the selector and return a `Result` with the associated error"]
#track_caller
#visibility fn fail<#(#original_generics_without_defaults,)* __T>(self) -> ::core::result::Result<__T, #parameterized_error_name>
where
#(#extended_where_clauses),*
{
::core::result::Result::Err(self.build())
}
}
}
}
fn generate_into_error(self, source_field: Option<&crate::SourceField>) -> TokenStream {
let crate_root = self.crate_root;
let error_constructor_name = self.error_constructor_name;
let original_generics_without_defaults = self.original_generics_without_defaults;
let parameterized_error_name = self.parameterized_error_name;
let parameterized_selector_name = self.parameterized_selector_name();
let user_field_generics = self.user_field_generics();
let extended_where_clauses = self.extended_where_clauses();
let transfer_user_fields = self.transfer_user_fields();
let construct_implicit_fields = if source_field.is_some() {
self.construct_implicit_fields_with_source()
} else {
self.construct_implicit_fields()
};
let (source_ty, transform_source, transfer_source_field) = match source_field {
Some(source_field) => {
let SourceInfo {
source_field_type,
transform_source,
transfer_source_field,
} = build_source_info(source_field);
(
quote! { #source_field_type },
Some(transform_source),
Some(transfer_source_field),
)
}
None => (quote! { #crate_root::NoneError }, None, None),
};
let track_caller = track_caller();
quote! {
impl<#(#original_generics_without_defaults,)* #(#user_field_generics,)*> #crate_root::IntoError<#parameterized_error_name> for #parameterized_selector_name
where
#parameterized_error_name: #crate_root::Error + #crate_root::ErrorCompat,
#(#extended_where_clauses),*
{
type Source = #source_ty;
#track_caller
fn into_error(self, error: Self::Source) -> #parameterized_error_name {
#transform_source;
#error_constructor_name {
#construct_implicit_fields
#transfer_source_field
#(#transfer_user_fields),*
}
}
}
}
}
fn generate_whatever(
self,
source_field: Option<&crate::SourceField>,
message_field: &crate::Field,
) -> TokenStream {
let crate_root = self.crate_root;
let parameterized_error_name = self.parameterized_error_name;
let error_constructor_name = self.error_constructor_name;
let construct_implicit_fields = self.construct_implicit_fields();
let construct_implicit_fields_with_source =
self.construct_implicit_fields_with_source();
let (source_ty, transfer_source_field, empty_source_field) = match source_field {
Some(f) => {
let source_field_type = f.transformation.source_ty();
let source_field_name = &f.name;
let source_transformation = f.transformation.transformation();
(
quote! { #source_field_type },
Some(quote! { #source_field_name: (#source_transformation)(error), }),
Some(quote! { #source_field_name: core::option::Option::None, }),
)
}
None => (quote! { #crate_root::NoneError }, None, None),
};
let message_field_name = &message_field.name;
let track_caller = track_caller();
quote! {
impl #crate_root::FromString for #parameterized_error_name {
type Source = #source_ty;
#track_caller
fn without_source(message: String) -> Self {
#error_constructor_name {
#construct_implicit_fields
#empty_source_field
#message_field_name: message,
}
}
#track_caller
fn with_source(error: Self::Source, message: String) -> Self {
#error_constructor_name {
#construct_implicit_fields_with_source
#transfer_source_field
#message_field_name: message,
}
}
}
}
}
fn generate_from_source(self, source_field: &crate::SourceField) -> TokenStream {
let parameterized_error_name = self.parameterized_error_name;
let error_constructor_name = self.error_constructor_name;
let construct_implicit_fields_with_source =
self.construct_implicit_fields_with_source();
let original_generics_without_defaults = self.original_generics_without_defaults;
let user_field_generics = self.user_field_generics();
let where_clauses = self.where_clauses;
let SourceInfo {
source_field_type,
transform_source,
transfer_source_field,
} = build_source_info(source_field);
let track_caller = track_caller();
quote! {
impl<#(#original_generics_without_defaults,)* #(#user_field_generics,)*> ::core::convert::From<#source_field_type> for #parameterized_error_name
where
#(#where_clauses),*
{
#track_caller
fn from(error: #source_field_type) -> Self {
#transform_source;
#error_constructor_name {
#construct_implicit_fields_with_source
#transfer_source_field
}
}
}
}
}
}
struct SourceInfo<'a> {
source_field_type: &'a syn::Type,
transform_source: TokenStream,
transfer_source_field: TokenStream,
}
fn build_source_info(source_field: &crate::SourceField) -> SourceInfo<'_> {
let source_field_name = source_field.name();
let source_field_type = source_field.transformation.source_ty();
let target_field_type = source_field.transformation.target_ty();
let source_transformation = source_field.transformation.transformation();
let transform_source =
quote! { let error: #target_field_type = (#source_transformation)(error) };
let transfer_source_field = quote! { #source_field_name: error, };
SourceInfo {
source_field_type,
transform_source,
transfer_source_field,
}
}
fn track_caller() -> proc_macro2::TokenStream {
if cfg!(feature = "rust_1_46") {
quote::quote! { #[track_caller] }
} else {
quote::quote! {}
}
}
}
pub mod display {
use super::StaticIdent;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use std::collections::BTreeSet;
const FORMATTER_ARG: StaticIdent = StaticIdent("__snafu_display_formatter");
pub(crate) struct Display<'a> {
pub(crate) arms: &'a [TokenStream],
pub(crate) original_generics: &'a [TokenStream],
pub(crate) parameterized_error_name: &'a dyn ToTokens,
pub(crate) where_clauses: &'a [TokenStream],
}
impl ToTokens for Display<'_> {
fn to_tokens(&self, stream: &mut TokenStream) {
let Self {
arms,
original_generics,
parameterized_error_name,
where_clauses,
} = *self;
let display_impl = quote! {
#[allow(single_use_lifetimes)]
impl<#(#original_generics),*> ::core::fmt::Display for #parameterized_error_name
where
#(#where_clauses),*
{
fn fmt(&self, #FORMATTER_ARG: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
#[allow(unused_variables)]
match *self {
#(#arms),*
}
}
}
};
stream.extend(display_impl);
}
}
pub(crate) struct DisplayMatchArm<'a> {
pub(crate) field_container: &'a crate::FieldContainer,
pub(crate) default_name: &'a dyn ToTokens,
pub(crate) display_format: Option<&'a crate::Display>,
pub(crate) doc_comment: Option<&'a crate::DocComment>,
pub(crate) pattern_ident: &'a dyn ToTokens,
pub(crate) selector_kind: &'a crate::ContextSelectorKind,
}
impl ToTokens for DisplayMatchArm<'_> {
fn to_tokens(&self, stream: &mut TokenStream) {
let Self {
field_container,
default_name,
display_format,
doc_comment,
pattern_ident,
selector_kind,
} = *self;
let source_field = selector_kind.source_field();
let mut shorthand_names = &BTreeSet::new();
let mut assigned_names = &BTreeSet::new();
let format = match (display_format, doc_comment, source_field) {
(Some(v), _, _) => {
let exprs = &v.exprs;
shorthand_names = &v.shorthand_names;
assigned_names = &v.assigned_names;
quote! { #(#exprs),* }
}
(_, Some(d), _) => {
let content = &d.content;
shorthand_names = &d.shorthand_names;
quote! { #content }
}
(_, _, Some(f)) => {
let field_name = &f.name;
quote! { concat!(stringify!(#default_name), ": {}"), #field_name }
}
_ => quote! { stringify!(#default_name)},
};
let field_names = super::AllFieldNames(field_container).field_names();
let shorthand_names = shorthand_names.iter().collect::<BTreeSet<_>>();
let assigned_names = assigned_names.iter().collect::<BTreeSet<_>>();
let shorthand_fields = &shorthand_names & &field_names;
let shorthand_fields = &shorthand_fields - &assigned_names;
let shorthand_assignments = quote! { #( #shorthand_fields = #shorthand_fields ),* };
let match_arm = quote! {
#pattern_ident { #(ref #field_names),* } => {
write!(#FORMATTER_ARG, #format, #shorthand_assignments)
}
};
stream.extend(match_arm);
}
}
}
pub mod error {
use super::StaticIdent;
use crate::{FieldContainer, Provide, SourceField};
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
pub(crate) const PROVIDE_ARG: StaticIdent = StaticIdent("__snafu_provide_demand");
pub(crate) struct Error<'a> {
pub(crate) crate_root: &'a dyn ToTokens,
pub(crate) description_arms: &'a [TokenStream],
pub(crate) original_generics: &'a [TokenStream],
pub(crate) parameterized_error_name: &'a dyn ToTokens,
pub(crate) provide_arms: &'a [TokenStream],
pub(crate) source_arms: &'a [TokenStream],
pub(crate) where_clauses: &'a [TokenStream],
}
impl ToTokens for Error<'_> {
fn to_tokens(&self, stream: &mut TokenStream) {
let Self {
crate_root,
description_arms,
original_generics,
parameterized_error_name,
provide_arms,
source_arms,
where_clauses,
} = *self;
let description_fn = quote! {
fn description(&self) -> &str {
match *self {
#(#description_arms)*
}
}
};
let source_body = quote! {
use #crate_root::AsErrorSource;
match *self {
#(#source_arms)*
}
};
let cause_fn = quote! {
fn cause(&self) -> ::core::option::Option<&dyn #crate_root::Error> {
#source_body
}
};
let source_fn = quote! {
fn source(&self) -> ::core::option::Option<&(dyn #crate_root::Error + 'static)> {
#source_body
}
};
let std_backtrace_fn = if cfg!(feature = "unstable-backtraces-impl-std") {
Some(quote! {
fn backtrace(&self) -> ::core::option::Option<&::std::backtrace::Backtrace> {
#crate_root::ErrorCompat::backtrace(self)
}
})
} else {
None
};
let provide_fn = if cfg!(feature = "unstable-provider-api") {
Some(quote! {
fn provide<'a>(&'a self, #PROVIDE_ARG: &mut core::any::Demand<'a>) {
match *self {
#(#provide_arms,)*
};
}
})
} else {
None
};
let error = quote! {
#[allow(single_use_lifetimes)]
impl<#(#original_generics),*> #crate_root::Error for #parameterized_error_name
where
Self: ::core::fmt::Debug + ::core::fmt::Display,
#(#where_clauses),*
{
#description_fn
#cause_fn
#source_fn
#std_backtrace_fn
#provide_fn
}
};
stream.extend(error);
}
}
pub(crate) struct ErrorSourceMatchArm<'a> {
pub(crate) field_container: &'a FieldContainer,
pub(crate) pattern_ident: &'a dyn ToTokens,
}
impl ToTokens for ErrorSourceMatchArm<'_> {
fn to_tokens(&self, stream: &mut TokenStream) {
let Self {
field_container: FieldContainer { selector_kind, .. },
pattern_ident,
} = *self;
let source_field = selector_kind.source_field();
let arm = match source_field {
Some(source_field) => {
let SourceField {
name: field_name, ..
} = source_field;
let convert_to_error_source = if selector_kind.is_whatever() {
quote! {
#field_name.as_ref().map(|e| e.as_error_source())
}
} else {
quote! {
::core::option::Option::Some(#field_name.as_error_source())
}
};
quote! {
#pattern_ident { ref #field_name, .. } => {
#convert_to_error_source
}
}
}
None => {
quote! {
#pattern_ident { .. } => { ::core::option::Option::None }
}
}
};
stream.extend(arm);
}
}
pub(crate) struct ProvidePlus<'a> {
provide: &'a Provide,
cached_name: proc_macro2::Ident,
}
pub(crate) struct ErrorProvideMatchArm<'a> {
pub(crate) crate_root: &'a dyn ToTokens,
pub(crate) field_container: &'a FieldContainer,
pub(crate) pattern_ident: &'a dyn ToTokens,
}
impl<'a> ToTokens for ErrorProvideMatchArm<'a> {
fn to_tokens(&self, stream: &mut TokenStream) {
let Self {
crate_root,
field_container,
pattern_ident,
} = *self;
let user_fields = field_container.user_fields();
let provides = enhance_provider_list(field_container.provides());
let field_names = super::AllFieldNames(field_container).field_names();
let (hi_explicit_calls, lo_explicit_calls) = build_explicit_provide_calls(&provides);
let cached_expressions = quote_cached_expressions(&provides);
let provide_refs = user_fields
.iter()
.chain(&field_container.implicit_fields)
.chain(field_container.selector_kind.message_field())
.flat_map(|f| {
if f.provide {
Some((&f.ty, f.name()))
} else {
None
}
});
let provided_source = field_container
.selector_kind
.source_field()
.filter(|f| f.provide);
let source_provide_ref =
provided_source.map(|f| (f.transformation.source_ty(), f.name()));
let provide_refs = provide_refs.chain(source_provide_ref);
let source_chain = provided_source.map(|f| {
let name = f.name();
quote! {
#name.provide(#PROVIDE_ARG);
}
});
let user_chained = quote_chained(&provides);
let shorthand_calls = provide_refs.map(|(ty, name)| {
quote! { #PROVIDE_ARG.provide_ref::<#ty>(#name) }
});
let provided_backtrace = field_container
.backtrace_field
.as_ref()
.filter(|f| f.provide);
let provide_backtrace = provided_backtrace.map(|f| {
let name = f.name();
quote! {
if #PROVIDE_ARG.would_be_satisfied_by_ref_of::<#crate_root::Backtrace>() {
if let ::core::option::Option::Some(bt) = #crate_root::AsBacktrace::as_backtrace(#name) {
#PROVIDE_ARG.provide_ref::<#crate_root::Backtrace>(bt);
}
}
}
});
let arm = quote! {
#pattern_ident { #(ref #field_names,)* .. } => {
#(#cached_expressions;)*
#(#hi_explicit_calls;)*
#source_chain;
#(#user_chained;)*
#provide_backtrace;
#(#shorthand_calls;)*
#(#lo_explicit_calls;)*
}
};
stream.extend(arm);
}
}
pub(crate) fn enhance_provider_list<'a>(provides: &'a [Provide]) -> Vec<ProvidePlus<'a>> {
provides
.iter()
.enumerate()
.map(|(i, provide)| {
let cached_name = format_ident!("__snafu_cached_expr_{}", i);
ProvidePlus {
provide,
cached_name,
}
})
.collect()
}
pub(crate) fn quote_cached_expressions<'a>(
provides: &'a [ProvidePlus<'a>],
) -> impl Iterator<Item = proc_macro2::TokenStream> + 'a {
provides.iter().filter(|pp| pp.provide.is_chain).map(|pp| {
let cached_name = &pp.cached_name;
let expr = &pp.provide.expr;
quote! {
let #cached_name = #expr;
}
})
}
pub(crate) fn quote_chained<'a>(
provides: &'a [ProvidePlus<'a>],
) -> impl Iterator<Item = proc_macro2::TokenStream> + 'a {
provides.iter().filter(|pp| pp.provide.is_chain).map(|pp| {
let arm = if pp.provide.is_opt {
quote! { ::core::option::Option::Some(chained_item) }
} else {
quote! { chained_item }
};
let cached_name = &pp.cached_name;
quote! {
if let #arm = #cached_name {
::core::any::Provider::provide(chained_item, #PROVIDE_ARG);
}
}
})
}
fn quote_provides<'a, I>(provides: I) -> impl Iterator<Item = proc_macro2::TokenStream> + 'a
where
I: IntoIterator<Item = &'a ProvidePlus<'a>>,
I::IntoIter: 'a,
{
provides.into_iter().map(|pp| {
let ProvidePlus {
provide:
Provide {
is_chain,
is_opt,
is_priority: _,
is_ref,
ty,
expr,
},
cached_name,
} = pp;
let effective_expr = if *is_chain {
quote! { #cached_name }
} else {
quote! { #expr }
};
match (is_opt, is_ref) {
(true, true) => {
quote! {
if #PROVIDE_ARG.would_be_satisfied_by_ref_of::<#ty>() {
if let ::core::option::Option::Some(v) = #effective_expr {
#PROVIDE_ARG.provide_ref::<#ty>(v);
}
}
}
}
(true, false) => {
quote! {
if #PROVIDE_ARG.would_be_satisfied_by_value_of::<#ty>() {
if let ::core::option::Option::Some(v) = #effective_expr {
#PROVIDE_ARG.provide_value::<#ty>(v);
}
}
}
}
(false, true) => {
quote! { #PROVIDE_ARG.provide_ref_with::<#ty>(|| #effective_expr) }
}
(false, false) => {
quote! { #PROVIDE_ARG.provide_value_with::<#ty>(|| #effective_expr) }
}
}
})
}
pub(crate) fn build_explicit_provide_calls<'a>(
provides: &'a [ProvidePlus<'a>],
) -> (
impl Iterator<Item = TokenStream> + 'a,
impl Iterator<Item = TokenStream> + 'a,
) {
let (high_priority, low_priority): (Vec<_>, Vec<_>) =
provides.iter().partition(|pp| pp.provide.is_priority);
let hi_explicit_calls = quote_provides(high_priority);
let lo_explicit_calls = quote_provides(low_priority);
(hi_explicit_calls, lo_explicit_calls)
}
}
pub mod error_compat {
use crate::{Field, FieldContainer, SourceField};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
pub(crate) struct ErrorCompat<'a> {
pub(crate) crate_root: &'a dyn ToTokens,
pub(crate) parameterized_error_name: &'a dyn ToTokens,
pub(crate) backtrace_arms: &'a [TokenStream],
pub(crate) original_generics: &'a [TokenStream],
pub(crate) where_clauses: &'a [TokenStream],
}
impl ToTokens for ErrorCompat<'_> {
fn to_tokens(&self, stream: &mut TokenStream) {
let Self {
crate_root,
parameterized_error_name,
backtrace_arms,
original_generics,
where_clauses,
} = *self;
let backtrace_fn = quote! {
fn backtrace(&self) -> ::core::option::Option<&#crate_root::Backtrace> {
match *self {
#(#backtrace_arms),*
}
}
};
let error_compat_impl = quote! {
#[allow(single_use_lifetimes)]
impl<#(#original_generics),*> #crate_root::ErrorCompat for #parameterized_error_name
where
#(#where_clauses),*
{
#backtrace_fn
}
};
stream.extend(error_compat_impl);
}
}
pub(crate) struct ErrorCompatBacktraceMatchArm<'a> {
pub(crate) crate_root: &'a dyn ToTokens,
pub(crate) field_container: &'a FieldContainer,
pub(crate) pattern_ident: &'a dyn ToTokens,
}
impl ToTokens for ErrorCompatBacktraceMatchArm<'_> {
fn to_tokens(&self, stream: &mut TokenStream) {
let Self {
crate_root,
field_container:
FieldContainer {
backtrace_field,
selector_kind,
..
},
pattern_ident,
} = *self;
let match_arm = match (selector_kind.source_field(), backtrace_field) {
(Some(source_field), _) if source_field.backtrace_delegate => {
let SourceField {
name: field_name, ..
} = source_field;
quote! {
#pattern_ident { ref #field_name, .. } => { #crate_root::ErrorCompat::backtrace(#field_name) }
}
}
(_, Some(backtrace_field)) => {
let Field {
name: field_name, ..
} = backtrace_field;
quote! {
#pattern_ident { ref #field_name, .. } => { #crate_root::AsBacktrace::as_backtrace(#field_name) }
}
}
_ => {
quote! {
#pattern_ident { .. } => { ::core::option::Option::None }
}
}
};
stream.extend(match_arm);
}
}
}