-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added support for thiserror
#18
Changes from all commits
db7d306
6767b51
167686f
fcea5e5
a3aa140
3c90a1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
use proc_macro2::TokenStream; | ||
use quote::{quote, ToTokens}; | ||
use syn::{Attribute, LitStr, Meta, Result}; | ||
use syn::{ | ||
spanned::Spanned, Attribute, Error, Lit::Str, LitStr, Meta, MetaList, MetaNameValue, | ||
NestedMeta, Result, | ||
}; | ||
|
||
pub struct Display { | ||
pub fmt: LitStr, | ||
|
@@ -18,27 +21,38 @@ impl ToTokens for Display { | |
} | ||
|
||
pub fn display(attrs: &[Attribute]) -> Result<Option<Display>> { | ||
for attr in attrs { | ||
if attr.path.is_ident("doc") { | ||
let meta = attr.parse_meta()?; | ||
let lit = match meta { | ||
Meta::NameValue(syn::MetaNameValue { | ||
lit: syn::Lit::Str(lit), | ||
.. | ||
}) => lit, | ||
_ => unimplemented!(), | ||
}; | ||
|
||
let lit = LitStr::new(lit.value().trim(), lit.span()); | ||
|
||
let attr = attrs | ||
.iter() | ||
.find(|attr| attr.path.is_ident("display")) | ||
.or_else(|| attrs.iter().find(|attr| attr.path.is_ident("doc"))); | ||
if let Some(attr) = attr { | ||
let meta = attr.parse_meta()?; | ||
let lit = match meta { | ||
Meta::NameValue(MetaNameValue { lit: Str(lit), .. }) => Some(Ok(lit)), | ||
Meta::List(MetaList { nested, .. }) => { | ||
nested.iter().find_map(|nested_attr| match nested_attr { | ||
NestedMeta::Meta(Meta::Path(path)) => { | ||
if path.is_ident("transparent") { | ||
Some(Ok(LitStr::new("{0}", attr.span()))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I think what we might want to do is modify enum DisplayFmt {
Provided(LitStr),
Transparent,
} That way we can differentiate between cases where we've been given a format string and cases where we need to figure it out ourselves. Then when we are constructing the actual display implementation for the type if the format is |
||
} else { | ||
Some(Err(Error::new_spanned(attr, "attr error"))) | ||
} | ||
} | ||
NestedMeta::Lit(Str(lit)) => Some(Ok(lit.clone())), | ||
_ => Some(Err(Error::new_spanned(attr, "cant accept the type"))), | ||
}) | ||
} | ||
_ => Some(Err(Error::new_spanned(attr, "namevalue or meta"))), | ||
}; | ||
if let Some(Ok(l)) = lit { | ||
let mut display = Display { | ||
fmt: lit, | ||
fmt: LitStr::new(l.value().trim(), l.span()), | ||
args: TokenStream::new(), | ||
}; | ||
|
||
display.expand_shorthand(); | ||
return Ok(Some(display)); | ||
} | ||
}; | ||
} | ||
|
||
Ok(None) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
use anyhow::anyhow; | ||
use displaydoc::Display; | ||
use std::error::Error as StdError; | ||
use std::io; | ||
use thiserror::Error; | ||
|
||
fn assert_display<T: std::fmt::Display>(input: T, expected: &'static str) { | ||
let out = format!("{}", input); | ||
assert_eq!(expected, out); | ||
} | ||
|
||
#[ignore] | ||
#[test] | ||
fn test_transparent_for_enum() { | ||
#[derive(Display, Error, Debug)] | ||
enum MyError { | ||
#[display(transparent)] | ||
/// Doc for Variant. | ||
Variant(anyhow::Error), | ||
} | ||
|
||
let var = MyError::Variant(anyhow!("inner").context("outer")); | ||
assert_display(&var, "outer"); | ||
assert_eq!(var.source().unwrap().to_string(), "inner") | ||
} | ||
|
||
#[test] | ||
fn test_transparent_for_struct() { | ||
#[derive(Display, Error, Debug)] | ||
#[error(transparent)] | ||
struct Error(ErrorKind); | ||
|
||
#[derive(Display, Error, Debug)] | ||
enum ErrorKind { | ||
#[display("E0")] | ||
E0, | ||
#[display("E1")] | ||
E1(#[from] io::Error), | ||
} | ||
|
||
let error = Error(ErrorKind::E0); | ||
assert_eq!("E0", error.to_string()); | ||
assert!(error.source().is_none()); | ||
|
||
let io = io::Error::new(io::ErrorKind::Other, "oh no!"); | ||
let error = Error(ErrorKind::from(io)); | ||
assert_eq!("E1", error.to_string()); | ||
error.source().unwrap().downcast_ref::<io::Error>().unwrap(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the only test failing now. |
||
} | ||
|
||
#[test] | ||
fn test_errordoc_for_enum() { | ||
#[derive(Display, Error, Debug)] | ||
enum MyError { | ||
/// I'm not a doc for Variant | ||
#[display("I'm a doc for Variant")] | ||
Variant, | ||
} | ||
assert_display(MyError::Variant, "I'm a doc for Variant"); | ||
} | ||
|
||
#[test] | ||
fn test_errordoc_for_struct() { | ||
#[derive(Display, Error, Debug)] | ||
#[display("I'm a doc for MyError")] | ||
struct MyError { | ||
/// I'm not a doc for MyError | ||
variant: u8, | ||
} | ||
assert_display(MyError { variant: 42 }, "I'm a doc for MyError"); | ||
} | ||
|
||
#[test] | ||
fn test_thiserror_implicit_source_works() { | ||
#[derive(Debug, Display, Error)] | ||
enum SourceError { | ||
#[display(transparent)] | ||
ImplicitSource { source: anyhow::Error }, | ||
#[display(transparent)] | ||
ExplicitSource { | ||
source: String, | ||
#[source] | ||
io: anyhow::Error, | ||
}, | ||
/// There isn't really a {source} | ||
DocSourceless { source: String }, | ||
} | ||
|
||
let implicit_source = SourceError::ImplicitSource { | ||
source: anyhow!("inner").context("outer"), | ||
}; | ||
let explicit_source = SourceError::ExplicitSource { | ||
source: "Error!!".to_string(), | ||
io: anyhow!("inner").context("outer"), | ||
}; | ||
let docsource_less = SourceError::DocSourceless { source: "ERROR!!".to_string()}; | ||
assert_display(&implicit_source, "outer"); | ||
// assert_display(&explicit_source, "outer"); | ||
// assert_display(&docsource_less, "ERROR!!"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you accidentally added cargo-expand as a dependency here