Skip to content
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

Add support for thiserror attributes #15

Open
yaahc opened this issue May 3, 2020 · 7 comments
Open

Add support for thiserror attributes #15

yaahc opened this issue May 3, 2020 · 7 comments

Comments

@yaahc
Copy link
Owner

yaahc commented May 3, 2020

To support better interop with thiserror displaydoc should attempt to first look for #[error("..")] and #[error(transparent)] attributes before looking for doc attributes, this will allow displaydoc to only use a subset of the doc comments for the display implementation so you can derive more complicated displays without ruining your documentation.

@yaahc yaahc added the good first issue Good for newcomers label May 11, 2020
@TakaakiFuruse
Copy link

TakaakiFuruse commented May 15, 2020

@yaahc
Hi, I just found you are calling for participation on This week in Rust. May I help you? Would you tell me more detail?

I just guessing you are thinking of doing kind like this?

use displaydoc::Display;
use thiserror::Error

#[derive(Error, Display)]
enum Happy {
    /// I really like Variant1
    #[error("Variant1 error!!")]
    /// Variant3 is okay {sometimes}
    #[error("Variant3 error !! ({sometimes})")]
    Variant3 { sometimes: &'static str },
}

fn main() {
format!("{}", Happy::Variant1) 
// => "I really like Variant1"

Happy::Variatn1
// => "Variant1 error!!"

format!("{}", Happy::Variant3 {someteims: "probably"}) 
// => "Variant3 is okey probably"

Happy::Variant1
// => "Variant3 error!! (probably)"
}

@yaahc
Copy link
Owner Author

yaahc commented May 15, 2020

I'm not sure about the format bit but it looks like you're on the right track. The idea would be that if we see an #[error("..")] attribute we want to use that instead of the first doc attribute. And if the content of the error attribute is transparent it should be equivalent to "{0}" I think.

Whatever we end up doing we should check against the upstream code in thiserror, which displaydoc was forked from. So any special case handling they do for #[error(transparent)] we would want to imitate, so that switching between using thiserror and displaydoc for your display implementation should produce the exact same output even without touching the attributes.

@TakaakiFuruse
Copy link

TakaakiFuruse commented May 15, 2020

How about this?

use displaydoc::Display;
use thiserror::Error;
use anyhou::anyhow;

.
.
.

#[derive(Display, Error)]
enum Happy {
.
.
.

    #[error(transparent)]
    Variant7(anyhow::Error),

    /// I'm not a doc for Variant8
    #[error("I'm a doc for Variatn8")]
    Variant8,
}

#[test]
fn thiserror_integration_var7() {
    let var7 = Happy::Variant7(anyhow!("inner").context("outer"));
    assert_display(var7, "outer");
    assert_eq!(var7.source().unwrap().to_string(), "innter")
}
#[test]
fn thiserror_integration_var8() {
    assert_display(Happy::Variant8, "I'm a doc for Variant8");
}

I need to study tests of thiserror more...

@yaahc
Copy link
Owner Author

yaahc commented May 15, 2020

Exactly, though you'd need to derive Error on Happy via thiserror for source() to work, but you get the point.

@TakaakiFuruse
Copy link

Error on Happy via thiserror fo
Yeah, I have corrected it. I forgot it

@TakaakiFuruse
Copy link

I'm struggling but currently have no good idea to fix it.
One big issue is how we can solve the conflict of Display trait.
Both displaydoc and thiserror needs each way of fmt.

So displaydoc has Display like this

fn impl_struct(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
.
.
.
        quote! {
            impl #impl_generics core::fmt::Display for #ty #ty_generics #where_clause {
                fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
                    #[allow(unused_variables)]
                    let #pat = self;
                    #display
                }
            }
        }
    });

    Ok(quote! { #display })
}

And thiserror is like this

fn impl_struct(input: Struct) -> TokenStream {
.
.
.
    let from_impl = input.from_field().map(|from_field| {
        let backtrace_field = input.backtrace_field();
        let from = from_field.ty;
        let body = from_initializer(from_field, backtrace_field);
        quote! {
            impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause {
                #[allow(deprecated)]
                fn from(source: #from) -> Self {
                    #ty #body
                }
            }
        }
    });
.
.
.

After my research it would be possible if we could use specialization, but that is not implemented on stable....

Do you have any good idead?

@yaahc
Copy link
Owner Author

yaahc commented May 17, 2020

I don't think we need to mess with the code generation at all, this should only be modifying the code that reads the attributes. If you open a PR with the changes you've made so far that might help me get a better idea of what approach you're taking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants