//! std::error::Error trait implementation generation for struct errors

use proc_macro2::TokenStream;
use quote::quote;

use crate::ir::StructDefinition;

/// Generate the std::error::Error trait implementation
pub fn generate_error_impl(ir: &StructDefinition) -> TokenStream {
    let name = &ir.name;
    let (impl_generics, ty_generics, where_clause) = ir.generics.split_for_impl();

    let source_field = ir.fields.iter().find(|f| f.is_source);

    let source_body = if let Some(field) = source_field {
        let field_name = &field.rust_name;
        quote! {
            {
                use ::apollo_errors::private::AsDynError as _;
                ::std::option::Option::Some(self.#field_name.as_dyn_error())
            }
        }
    } else {
        quote! { ::std::option::Option::None }
    };

    quote! {
        #[automatically_derived]
        impl #impl_generics ::std::error::Error for #name #ty_generics #where_clause {
            fn source(&self) -> ::std::option::Option<&(dyn ::std::error::Error + 'static)> {
                #source_body
            }
        }
    }
}
