Skip to content

Commit

Permalink
Add GenerateImplicitData::generate_with_source
Browse files Browse the repository at this point in the history
This allows implicit data to inspect underlying error causes to avoid
creating redundant data.

Closes #349
  • Loading branch information
shepmaster committed Oct 9, 2022
1 parent e2b29c3 commit 0ce4e1a
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 8 deletions.
45 changes: 37 additions & 8 deletions snafu-derive/src/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,35 @@ pub mod context_selector {
}

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 crate_root = self.crate_root;
let name = &field.name;
quote! { #name: #crate_root::GenerateImplicitData::generate(), }
quote! { #name: #expression, }
})
.collect()
}
Expand Down Expand Up @@ -286,7 +308,11 @@ pub mod context_selector {
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 = self.construct_implicit_fields();
let construct_implicit_fields = if source_field.is_some() {
self.construct_implicit_fields_with_source()
} else {
self.construct_implicit_fields()
};

let (source_ty, transfer_source_field) = match source_field {
Some(source_field) => {
Expand All @@ -309,8 +335,8 @@ pub mod context_selector {
#track_caller
fn into_error(self, error: Self::Source) -> #parameterized_error_name {
#error_constructor_name {
#transfer_source_field
#construct_implicit_fields
#transfer_source_field
#(#transfer_user_fields),*
}
}
Expand All @@ -327,6 +353,8 @@ pub mod context_selector {
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();

// testme: transform

Expand Down Expand Up @@ -356,18 +384,18 @@ pub mod context_selector {
#track_caller
fn without_source(message: String) -> Self {
#error_constructor_name {
#construct_implicit_fields
#empty_source_field
#message_field_name: message,
#construct_implicit_fields
}
}

#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,
#construct_implicit_fields
}
}
}
Expand All @@ -377,7 +405,8 @@ pub mod context_selector {
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 = self.construct_implicit_fields();
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;
Expand All @@ -394,8 +423,8 @@ pub mod context_selector {
#track_caller
fn from(error: #source_field_type) -> Self {
#error_constructor_name {
#construct_implicit_fields_with_source
#transfer_source_field
#construct_implicit_fields
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,16 @@ pub trait FromString {
pub trait GenerateImplicitData {
/// Build the data.
fn generate() -> Self;

/// Build the data using the given source
#[cfg_attr(feature = "rust_1_46", track_caller)]
fn generate_with_source(source: &dyn crate::Error) -> Self
where
Self: Sized,
{
let _source = source;
Self::generate()
}
}

/// View a backtrace-like value as an optional backtrace.
Expand Down
93 changes: 93 additions & 0 deletions tests/implicit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,96 @@ mod multiple_fields {
assert_eq!(one, two);
}
}

mod with_and_without_source {
use snafu::{prelude::*, FromString, IntoError};

#[derive(Debug, PartialEq)]
enum ItWas {
Generate,
GenerateWithSource,
}

#[derive(Debug)]
struct ImplicitData(ItWas);

impl snafu::GenerateImplicitData for ImplicitData {
fn generate() -> Self {
Self(ItWas::Generate)
}

fn generate_with_source(_: &dyn snafu::Error) -> Self {
Self(ItWas::GenerateWithSource)
}
}

#[derive(Debug, Snafu)]
struct InnerError;

#[derive(Debug, Snafu)]
struct HasSource {
source: InnerError,
#[snafu(implicit)]
data: ImplicitData,
}

#[derive(Debug, Snafu)]
struct NoSource {
#[snafu(implicit)]
data: ImplicitData,
}

#[derive(Debug, Snafu)]
#[snafu(context(false))]
struct HasSourceNoContext {
source: InnerError,
#[snafu(implicit)]
data: ImplicitData,
}

#[derive(Debug, Snafu)]
#[snafu(whatever, display("{message}"))]
struct MyOwnWhatever {
message: String,
#[snafu(source(from(Box<dyn std::error::Error>, Some)))]
source: Option<Box<dyn std::error::Error>>,
#[snafu(implicit)]
data: ImplicitData,
}

#[test]
fn calls_generate_for_no_source() {
let e = NoSourceSnafu.build();
assert_eq!(e.data.0, ItWas::Generate);
}

#[test]
fn calls_generate_with_source_for_source() {
let e = HasSourceSnafu.into_error(InnerError);
assert_eq!(e.data.0, ItWas::GenerateWithSource);
}

#[test]
fn calls_generate_for_none() {
let e = NoSourceSnafu.into_error(snafu::NoneError);
assert_eq!(e.data.0, ItWas::Generate);
}

#[test]
fn calls_generate_with_source_for_no_context() {
let e = HasSourceNoContext::from(InnerError);
assert_eq!(e.data.0, ItWas::GenerateWithSource);
}

#[test]
fn calls_generate_for_whatever_with_no_source() {
let e = MyOwnWhatever::without_source("bang".into());
assert_eq!(e.data.0, ItWas::Generate);
}

#[test]
fn calls_generate_with_source_for_whatever_with_source() {
let e = MyOwnWhatever::with_source(Box::new(InnerError), "bang".into());
assert_eq!(e.data.0, ItWas::GenerateWithSource);
}
}

0 comments on commit 0ce4e1a

Please sign in to comment.