Skip to content

Commit

Permalink
decouple box impl with ctors
Browse files Browse the repository at this point in the history
Signed-off-by: Bugen Zhao <[email protected]>
  • Loading branch information
BugenZhao committed Nov 23, 2023
1 parent df4aca8 commit 2f293b6
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 70 deletions.
155 changes: 89 additions & 66 deletions derive/src/expand.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::{spanned::Spanned, DeriveInput, GenericArgument, Member, PathArguments, Result, Type};
use syn::{
spanned::Spanned, DeriveInput, GenericArgument, Ident, Member, PathArguments, Result, Type,
};

use crate::thiserror::ast::{Input, Variant};

Expand All @@ -15,7 +17,7 @@ enum SourceInto {
No,
}

fn resolve(variant: &Variant<'_>, source_into: SourceInto) -> Args {
fn resolve_variant_args(variant: &Variant<'_>, source_into: SourceInto) -> Args {
let mut other_args = Vec::new();
let mut source_arg = None;
let mut ctor_args = Vec::new();
Expand Down Expand Up @@ -70,17 +72,15 @@ fn resolve(variant: &Variant<'_>, source_into: SourceInto) -> Args {
}
}

pub enum DeriveType {
Construct,
ContextInto,
Box,
struct DeriveMeta {
impl_type: Ident,
backtrace: bool,
}

pub fn derive(input: &DeriveInput, t: DeriveType) -> Result<TokenStream> {
let input_type = input.ident.clone();

fn resolve_meta(input: &DeriveInput) -> Result<DeriveMeta> {
let mut impl_type = None;
let mut backtrace = false;

for attr in &input.attrs {
if attr.path().is_ident("thiserror_ext") {
attr.parse_nested_meta(|meta| {
Expand All @@ -95,63 +95,89 @@ pub fn derive(input: &DeriveInput, t: DeriveType) -> Result<TokenStream> {
})?;
}
}
let impl_type = impl_type.unwrap_or_else(|| input_type.clone());
let impl_type = impl_type.unwrap_or_else(|| input.ident.clone());

Ok(DeriveMeta {
impl_type,
backtrace,
})
}

pub enum DeriveCtorType {
Construct,
ContextInto,
}

pub fn derive_box(input: &DeriveInput) -> Result<TokenStream> {
let input_type = input.ident.clone();
let vis = &input.vis;

if let DeriveType::Box = t {
if impl_type == input_type {
return Err(syn::Error::new_spanned(
input,
"should specify a different type for `Box` derive with `#[thiserror_ext(type = <type>)]`",
));
}
let DeriveMeta {
impl_type,
backtrace,
} = resolve_meta(input)?;

let backtrace_type_param = if backtrace {
quote!(thiserror_ext::__private::MaybeBacktrace)
} else {
quote!(thiserror_ext::__private::NoBacktrace)
};
if impl_type == input_type {
return Err(syn::Error::new_spanned(
input,
"should specify a different type for `Box` derive with `#[thiserror_ext(type = <type>)]`",
));
}

let doc = format!("The boxed type of [`{}`].", input_type);
let generated = quote!(
#[doc = #doc]
#[derive(thiserror_ext::__private::thiserror::Error, Debug)]
#[error(transparent)]
#vis struct #impl_type(
#[from]
#[backtrace]
thiserror_ext::__private::ErrorBox<
#input_type,
#backtrace_type_param,
>,
);

// For `?` to work.
impl<E> From<E> for #impl_type
where
E: Into<#input_type>,
{
fn from(error: E) -> Self {
Self(thiserror_ext::__private::ErrorBox::new(error.into()))
}
let backtrace_type_param = if backtrace {
quote!(thiserror_ext::__private::MaybeBacktrace)
} else {
quote!(thiserror_ext::__private::NoBacktrace)
};

let doc = format!("The boxed type of [`{}`].", input_type);
let generated = quote!(
#[doc = #doc]
#[derive(thiserror_ext::__private::thiserror::Error, Debug)]
#[error(transparent)]
#vis struct #impl_type(
#[from]
#[backtrace]
thiserror_ext::__private::ErrorBox<
#input_type,
#backtrace_type_param,
>,
);

// For `?` to work.
impl<E> From<E> for #impl_type
where
E: Into<#input_type>,
{
fn from(error: E) -> Self {
Self(thiserror_ext::__private::ErrorBox::new(error.into()))
}
}

impl #impl_type {
#[doc = "Returns the reference to the inner error."]
#vis fn inner(&self) -> &#input_type {
self.0.inner()
}
impl #impl_type {
#[doc = "Returns the reference to the inner error."]
#vis fn inner(&self) -> &#input_type {
self.0.inner()
}

#[doc = "Consumes `self` and returns the inner error."]
#vis fn into_inner(self) -> #input_type {
self.0.into_inner()
}
#[doc = "Consumes `self` and returns the inner error."]
#vis fn into_inner(self) -> #input_type {
self.0.into_inner()
}
);
}
);

return Ok(generated);
}
Ok(generated)
}

pub fn derive_ctor(input: &DeriveInput, t: DeriveCtorType) -> Result<TokenStream> {
let input_type = input.ident.clone();
let vis = &input.vis;

let DeriveMeta {
impl_type,
backtrace: _,
} = resolve_meta(input)?;

let input = Input::from_syn(input)?;

Expand Down Expand Up @@ -179,12 +205,11 @@ pub fn derive(input: &DeriveInput, t: DeriveType) -> Result<TokenStream> {
other_args,
source_arg,
ctor_args,
} = resolve(
} = resolve_variant_args(
&variant,
match t {
DeriveType::Construct => SourceInto::Yes,
DeriveType::ContextInto => SourceInto::No,
DeriveType::Box => unreachable!(),
DeriveCtorType::Construct => SourceInto::Yes,
DeriveCtorType::ContextInto => SourceInto::No,
},
);

Expand All @@ -193,7 +218,7 @@ pub fn derive(input: &DeriveInput, t: DeriveType) -> Result<TokenStream> {
});

let item = match t {
DeriveType::Construct => {
DeriveCtorType::Construct => {
let ctor_name = format_ident!(
"{}",
big_camel_case_to_snake_case(&variant_name.to_string()),
Expand All @@ -208,7 +233,7 @@ pub fn derive(input: &DeriveInput, t: DeriveType) -> Result<TokenStream> {
}
)
}
DeriveType::ContextInto => {
DeriveCtorType::ContextInto => {
// It's implemented on `Result<T, SourceError>`, so there's must be the `source` field,
// and we expect there's at least one argument.
if source_arg.is_none() || other_args.is_empty() {
Expand Down Expand Up @@ -254,25 +279,23 @@ pub fn derive(input: &DeriveInput, t: DeriveType) -> Result<TokenStream> {
}
)
}
DeriveType::Box => unreachable!(),
};

items.push(item);
}

let generated = match t {
DeriveType::Construct => {
DeriveCtorType::Construct => {
quote!(
#[automatically_derived]
impl #impl_type {
#(#items)*
}
)
}
DeriveType::ContextInto => {
DeriveCtorType::ContextInto => {
quote!(#(#items)*)
}
DeriveType::Box => unreachable!(),
};

Ok(generated)
Expand Down
8 changes: 4 additions & 4 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use expand::DeriveType;
use expand::DeriveCtorType;
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};

Expand All @@ -9,7 +9,7 @@ mod thiserror;
pub fn derive_constructor(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);

expand::derive(&input, DeriveType::Construct)
expand::derive_ctor(&input, DeriveCtorType::Construct)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Expand All @@ -18,7 +18,7 @@ pub fn derive_constructor(input: TokenStream) -> TokenStream {
pub fn derive_context_into(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);

expand::derive(&input, DeriveType::ContextInto)
expand::derive_ctor(&input, DeriveCtorType::ContextInto)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Expand All @@ -27,7 +27,7 @@ pub fn derive_context_into(input: TokenStream) -> TokenStream {
pub fn derive_box(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);

expand::derive(&input, DeriveType::Box)
expand::derive_box(&input)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

0 comments on commit 2f293b6

Please sign in to comment.