133d722a9Sopenharmony_ciuse crate::syntax::{derive, Enum, Struct, Trait};
233d722a9Sopenharmony_ciuse proc_macro2::{Ident, Span, TokenStream};
333d722a9Sopenharmony_ciuse quote::{quote, quote_spanned, ToTokens};
433d722a9Sopenharmony_ci
533d722a9Sopenharmony_cipub use crate::syntax::derive::*;
633d722a9Sopenharmony_ci
733d722a9Sopenharmony_cipub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream {
833d722a9Sopenharmony_ci    let mut expanded = TokenStream::new();
933d722a9Sopenharmony_ci    let mut traits = Vec::new();
1033d722a9Sopenharmony_ci
1133d722a9Sopenharmony_ci    for derive in &strct.derives {
1233d722a9Sopenharmony_ci        let span = derive.span;
1333d722a9Sopenharmony_ci        match derive.what {
1433d722a9Sopenharmony_ci            Trait::Copy => expanded.extend(struct_copy(strct, span)),
1533d722a9Sopenharmony_ci            Trait::Clone => expanded.extend(struct_clone(strct, span)),
1633d722a9Sopenharmony_ci            Trait::Debug => expanded.extend(struct_debug(strct, span)),
1733d722a9Sopenharmony_ci            Trait::Default => expanded.extend(struct_default(strct, span)),
1833d722a9Sopenharmony_ci            Trait::Eq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)),
1933d722a9Sopenharmony_ci            Trait::ExternType => unreachable!(),
2033d722a9Sopenharmony_ci            Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
2133d722a9Sopenharmony_ci            Trait::Ord => expanded.extend(struct_ord(strct, span)),
2233d722a9Sopenharmony_ci            Trait::PartialEq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)),
2333d722a9Sopenharmony_ci            Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
2433d722a9Sopenharmony_ci            Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
2533d722a9Sopenharmony_ci            Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
2633d722a9Sopenharmony_ci        }
2733d722a9Sopenharmony_ci    }
2833d722a9Sopenharmony_ci
2933d722a9Sopenharmony_ci    if traits.is_empty() {
3033d722a9Sopenharmony_ci        *actual_derives = None;
3133d722a9Sopenharmony_ci    } else {
3233d722a9Sopenharmony_ci        *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
3333d722a9Sopenharmony_ci    }
3433d722a9Sopenharmony_ci
3533d722a9Sopenharmony_ci    expanded
3633d722a9Sopenharmony_ci}
3733d722a9Sopenharmony_ci
3833d722a9Sopenharmony_cipub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
3933d722a9Sopenharmony_ci    let mut expanded = TokenStream::new();
4033d722a9Sopenharmony_ci    let mut traits = Vec::new();
4133d722a9Sopenharmony_ci    let mut has_copy = false;
4233d722a9Sopenharmony_ci    let mut has_clone = false;
4333d722a9Sopenharmony_ci    let mut has_eq = false;
4433d722a9Sopenharmony_ci    let mut has_partial_eq = false;
4533d722a9Sopenharmony_ci
4633d722a9Sopenharmony_ci    for derive in &enm.derives {
4733d722a9Sopenharmony_ci        let span = derive.span;
4833d722a9Sopenharmony_ci        match derive.what {
4933d722a9Sopenharmony_ci            Trait::Copy => {
5033d722a9Sopenharmony_ci                expanded.extend(enum_copy(enm, span));
5133d722a9Sopenharmony_ci                has_copy = true;
5233d722a9Sopenharmony_ci            }
5333d722a9Sopenharmony_ci            Trait::Clone => {
5433d722a9Sopenharmony_ci                expanded.extend(enum_clone(enm, span));
5533d722a9Sopenharmony_ci                has_clone = true;
5633d722a9Sopenharmony_ci            }
5733d722a9Sopenharmony_ci            Trait::Debug => expanded.extend(enum_debug(enm, span)),
5833d722a9Sopenharmony_ci            Trait::Default => unreachable!(),
5933d722a9Sopenharmony_ci            Trait::Eq => {
6033d722a9Sopenharmony_ci                traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq));
6133d722a9Sopenharmony_ci                has_eq = true;
6233d722a9Sopenharmony_ci            }
6333d722a9Sopenharmony_ci            Trait::ExternType => unreachable!(),
6433d722a9Sopenharmony_ci            Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
6533d722a9Sopenharmony_ci            Trait::Ord => expanded.extend(enum_ord(enm, span)),
6633d722a9Sopenharmony_ci            Trait::PartialEq => {
6733d722a9Sopenharmony_ci                traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq));
6833d722a9Sopenharmony_ci                has_partial_eq = true;
6933d722a9Sopenharmony_ci            }
7033d722a9Sopenharmony_ci            Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
7133d722a9Sopenharmony_ci            Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
7233d722a9Sopenharmony_ci            Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
7333d722a9Sopenharmony_ci        }
7433d722a9Sopenharmony_ci    }
7533d722a9Sopenharmony_ci
7633d722a9Sopenharmony_ci    let span = enm.name.rust.span();
7733d722a9Sopenharmony_ci    if !has_copy {
7833d722a9Sopenharmony_ci        expanded.extend(enum_copy(enm, span));
7933d722a9Sopenharmony_ci    }
8033d722a9Sopenharmony_ci    if !has_clone {
8133d722a9Sopenharmony_ci        expanded.extend(enum_clone(enm, span));
8233d722a9Sopenharmony_ci    }
8333d722a9Sopenharmony_ci    if !has_eq {
8433d722a9Sopenharmony_ci        // Required to be derived in order for the enum's "variants" to be
8533d722a9Sopenharmony_ci        // usable in patterns.
8633d722a9Sopenharmony_ci        traits.push(quote!(::cxx::core::cmp::Eq));
8733d722a9Sopenharmony_ci    }
8833d722a9Sopenharmony_ci    if !has_partial_eq {
8933d722a9Sopenharmony_ci        traits.push(quote!(::cxx::core::cmp::PartialEq));
9033d722a9Sopenharmony_ci    }
9133d722a9Sopenharmony_ci
9233d722a9Sopenharmony_ci    *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
9333d722a9Sopenharmony_ci
9433d722a9Sopenharmony_ci    expanded
9533d722a9Sopenharmony_ci}
9633d722a9Sopenharmony_ci
9733d722a9Sopenharmony_cifn struct_copy(strct: &Struct, span: Span) -> TokenStream {
9833d722a9Sopenharmony_ci    let ident = &strct.name.rust;
9933d722a9Sopenharmony_ci    let generics = &strct.generics;
10033d722a9Sopenharmony_ci
10133d722a9Sopenharmony_ci    quote_spanned! {span=>
10233d722a9Sopenharmony_ci        impl #generics ::cxx::core::marker::Copy for #ident #generics {}
10333d722a9Sopenharmony_ci    }
10433d722a9Sopenharmony_ci}
10533d722a9Sopenharmony_ci
10633d722a9Sopenharmony_cifn struct_clone(strct: &Struct, span: Span) -> TokenStream {
10733d722a9Sopenharmony_ci    let ident = &strct.name.rust;
10833d722a9Sopenharmony_ci    let generics = &strct.generics;
10933d722a9Sopenharmony_ci
11033d722a9Sopenharmony_ci    let body = if derive::contains(&strct.derives, Trait::Copy) {
11133d722a9Sopenharmony_ci        quote!(*self)
11233d722a9Sopenharmony_ci    } else {
11333d722a9Sopenharmony_ci        let fields = strct.fields.iter().map(|field| &field.name.rust);
11433d722a9Sopenharmony_ci        let values = strct.fields.iter().map(|field| {
11533d722a9Sopenharmony_ci            let ident = &field.name.rust;
11633d722a9Sopenharmony_ci            let ty = field.ty.to_token_stream();
11733d722a9Sopenharmony_ci            let span = ty.into_iter().last().unwrap().span();
11833d722a9Sopenharmony_ci            quote_spanned!(span=> &self.#ident)
11933d722a9Sopenharmony_ci        });
12033d722a9Sopenharmony_ci        quote_spanned!(span=> #ident {
12133d722a9Sopenharmony_ci            #(#fields: ::cxx::core::clone::Clone::clone(#values),)*
12233d722a9Sopenharmony_ci        })
12333d722a9Sopenharmony_ci    };
12433d722a9Sopenharmony_ci
12533d722a9Sopenharmony_ci    quote_spanned! {span=>
12633d722a9Sopenharmony_ci        #[allow(clippy::expl_impl_clone_on_copy)]
12733d722a9Sopenharmony_ci        impl #generics ::cxx::core::clone::Clone for #ident #generics {
12833d722a9Sopenharmony_ci            fn clone(&self) -> Self {
12933d722a9Sopenharmony_ci                #body
13033d722a9Sopenharmony_ci            }
13133d722a9Sopenharmony_ci        }
13233d722a9Sopenharmony_ci    }
13333d722a9Sopenharmony_ci}
13433d722a9Sopenharmony_ci
13533d722a9Sopenharmony_cifn struct_debug(strct: &Struct, span: Span) -> TokenStream {
13633d722a9Sopenharmony_ci    let ident = &strct.name.rust;
13733d722a9Sopenharmony_ci    let generics = &strct.generics;
13833d722a9Sopenharmony_ci    let struct_name = ident.to_string();
13933d722a9Sopenharmony_ci    let fields = strct.fields.iter().map(|field| &field.name.rust);
14033d722a9Sopenharmony_ci    let field_names = fields.clone().map(Ident::to_string);
14133d722a9Sopenharmony_ci
14233d722a9Sopenharmony_ci    quote_spanned! {span=>
14333d722a9Sopenharmony_ci        impl #generics ::cxx::core::fmt::Debug for #ident #generics {
14433d722a9Sopenharmony_ci            fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
14533d722a9Sopenharmony_ci                formatter.debug_struct(#struct_name)
14633d722a9Sopenharmony_ci                    #(.field(#field_names, &self.#fields))*
14733d722a9Sopenharmony_ci                    .finish()
14833d722a9Sopenharmony_ci            }
14933d722a9Sopenharmony_ci        }
15033d722a9Sopenharmony_ci    }
15133d722a9Sopenharmony_ci}
15233d722a9Sopenharmony_ci
15333d722a9Sopenharmony_cifn struct_default(strct: &Struct, span: Span) -> TokenStream {
15433d722a9Sopenharmony_ci    let ident = &strct.name.rust;
15533d722a9Sopenharmony_ci    let generics = &strct.generics;
15633d722a9Sopenharmony_ci    let fields = strct.fields.iter().map(|field| &field.name.rust);
15733d722a9Sopenharmony_ci
15833d722a9Sopenharmony_ci    quote_spanned! {span=>
15933d722a9Sopenharmony_ci        #[allow(clippy::derivable_impls)] // different spans than the derived impl
16033d722a9Sopenharmony_ci        impl #generics ::cxx::core::default::Default for #ident #generics {
16133d722a9Sopenharmony_ci            fn default() -> Self {
16233d722a9Sopenharmony_ci                #ident {
16333d722a9Sopenharmony_ci                    #(
16433d722a9Sopenharmony_ci                        #fields: ::cxx::core::default::Default::default(),
16533d722a9Sopenharmony_ci                    )*
16633d722a9Sopenharmony_ci                }
16733d722a9Sopenharmony_ci            }
16833d722a9Sopenharmony_ci        }
16933d722a9Sopenharmony_ci    }
17033d722a9Sopenharmony_ci}
17133d722a9Sopenharmony_ci
17233d722a9Sopenharmony_cifn struct_ord(strct: &Struct, span: Span) -> TokenStream {
17333d722a9Sopenharmony_ci    let ident = &strct.name.rust;
17433d722a9Sopenharmony_ci    let generics = &strct.generics;
17533d722a9Sopenharmony_ci    let fields = strct.fields.iter().map(|field| &field.name.rust);
17633d722a9Sopenharmony_ci
17733d722a9Sopenharmony_ci    quote_spanned! {span=>
17833d722a9Sopenharmony_ci        impl #generics ::cxx::core::cmp::Ord for #ident #generics {
17933d722a9Sopenharmony_ci            fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
18033d722a9Sopenharmony_ci                #(
18133d722a9Sopenharmony_ci                    match ::cxx::core::cmp::Ord::cmp(&self.#fields, &other.#fields) {
18233d722a9Sopenharmony_ci                        ::cxx::core::cmp::Ordering::Equal => {}
18333d722a9Sopenharmony_ci                        ordering => return ordering,
18433d722a9Sopenharmony_ci                    }
18533d722a9Sopenharmony_ci                )*
18633d722a9Sopenharmony_ci                ::cxx::core::cmp::Ordering::Equal
18733d722a9Sopenharmony_ci            }
18833d722a9Sopenharmony_ci        }
18933d722a9Sopenharmony_ci    }
19033d722a9Sopenharmony_ci}
19133d722a9Sopenharmony_ci
19233d722a9Sopenharmony_cifn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
19333d722a9Sopenharmony_ci    let ident = &strct.name.rust;
19433d722a9Sopenharmony_ci    let generics = &strct.generics;
19533d722a9Sopenharmony_ci
19633d722a9Sopenharmony_ci    let body = if derive::contains(&strct.derives, Trait::Ord) {
19733d722a9Sopenharmony_ci        quote! {
19833d722a9Sopenharmony_ci            ::cxx::core::option::Option::Some(::cxx::core::cmp::Ord::cmp(self, other))
19933d722a9Sopenharmony_ci        }
20033d722a9Sopenharmony_ci    } else {
20133d722a9Sopenharmony_ci        let fields = strct.fields.iter().map(|field| &field.name.rust);
20233d722a9Sopenharmony_ci        quote! {
20333d722a9Sopenharmony_ci            #(
20433d722a9Sopenharmony_ci                match ::cxx::core::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
20533d722a9Sopenharmony_ci                    ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) => {}
20633d722a9Sopenharmony_ci                    ordering => return ordering,
20733d722a9Sopenharmony_ci                }
20833d722a9Sopenharmony_ci            )*
20933d722a9Sopenharmony_ci            ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal)
21033d722a9Sopenharmony_ci        }
21133d722a9Sopenharmony_ci    };
21233d722a9Sopenharmony_ci
21333d722a9Sopenharmony_ci    quote_spanned! {span=>
21433d722a9Sopenharmony_ci        impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics {
21533d722a9Sopenharmony_ci            fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
21633d722a9Sopenharmony_ci                #body
21733d722a9Sopenharmony_ci            }
21833d722a9Sopenharmony_ci        }
21933d722a9Sopenharmony_ci    }
22033d722a9Sopenharmony_ci}
22133d722a9Sopenharmony_ci
22233d722a9Sopenharmony_cifn enum_copy(enm: &Enum, span: Span) -> TokenStream {
22333d722a9Sopenharmony_ci    let ident = &enm.name.rust;
22433d722a9Sopenharmony_ci
22533d722a9Sopenharmony_ci    quote_spanned! {span=>
22633d722a9Sopenharmony_ci        impl ::cxx::core::marker::Copy for #ident {}
22733d722a9Sopenharmony_ci    }
22833d722a9Sopenharmony_ci}
22933d722a9Sopenharmony_ci
23033d722a9Sopenharmony_cifn enum_clone(enm: &Enum, span: Span) -> TokenStream {
23133d722a9Sopenharmony_ci    let ident = &enm.name.rust;
23233d722a9Sopenharmony_ci
23333d722a9Sopenharmony_ci    quote_spanned! {span=>
23433d722a9Sopenharmony_ci        #[allow(clippy::expl_impl_clone_on_copy)]
23533d722a9Sopenharmony_ci        impl ::cxx::core::clone::Clone for #ident {
23633d722a9Sopenharmony_ci            fn clone(&self) -> Self {
23733d722a9Sopenharmony_ci                *self
23833d722a9Sopenharmony_ci            }
23933d722a9Sopenharmony_ci        }
24033d722a9Sopenharmony_ci    }
24133d722a9Sopenharmony_ci}
24233d722a9Sopenharmony_ci
24333d722a9Sopenharmony_cifn enum_debug(enm: &Enum, span: Span) -> TokenStream {
24433d722a9Sopenharmony_ci    let ident = &enm.name.rust;
24533d722a9Sopenharmony_ci    let variants = enm.variants.iter().map(|variant| {
24633d722a9Sopenharmony_ci        let variant = &variant.name.rust;
24733d722a9Sopenharmony_ci        let name = variant.to_string();
24833d722a9Sopenharmony_ci        quote_spanned! {span=>
24933d722a9Sopenharmony_ci            #ident::#variant => formatter.write_str(#name),
25033d722a9Sopenharmony_ci        }
25133d722a9Sopenharmony_ci    });
25233d722a9Sopenharmony_ci    let fallback = format!("{}({{}})", ident);
25333d722a9Sopenharmony_ci
25433d722a9Sopenharmony_ci    quote_spanned! {span=>
25533d722a9Sopenharmony_ci        impl ::cxx::core::fmt::Debug for #ident {
25633d722a9Sopenharmony_ci            fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
25733d722a9Sopenharmony_ci                match *self {
25833d722a9Sopenharmony_ci                    #(#variants)*
25933d722a9Sopenharmony_ci                    _ => ::cxx::core::write!(formatter, #fallback, self.repr),
26033d722a9Sopenharmony_ci                }
26133d722a9Sopenharmony_ci            }
26233d722a9Sopenharmony_ci        }
26333d722a9Sopenharmony_ci    }
26433d722a9Sopenharmony_ci}
26533d722a9Sopenharmony_ci
26633d722a9Sopenharmony_cifn enum_ord(enm: &Enum, span: Span) -> TokenStream {
26733d722a9Sopenharmony_ci    let ident = &enm.name.rust;
26833d722a9Sopenharmony_ci
26933d722a9Sopenharmony_ci    quote_spanned! {span=>
27033d722a9Sopenharmony_ci        impl ::cxx::core::cmp::Ord for #ident {
27133d722a9Sopenharmony_ci            fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
27233d722a9Sopenharmony_ci                ::cxx::core::cmp::Ord::cmp(&self.repr, &other.repr)
27333d722a9Sopenharmony_ci            }
27433d722a9Sopenharmony_ci        }
27533d722a9Sopenharmony_ci    }
27633d722a9Sopenharmony_ci}
27733d722a9Sopenharmony_ci
27833d722a9Sopenharmony_cifn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
27933d722a9Sopenharmony_ci    let ident = &enm.name.rust;
28033d722a9Sopenharmony_ci
28133d722a9Sopenharmony_ci    quote_spanned! {span=>
28233d722a9Sopenharmony_ci        impl ::cxx::core::cmp::PartialOrd for #ident {
28333d722a9Sopenharmony_ci            fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
28433d722a9Sopenharmony_ci                ::cxx::core::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
28533d722a9Sopenharmony_ci            }
28633d722a9Sopenharmony_ci        }
28733d722a9Sopenharmony_ci    }
28833d722a9Sopenharmony_ci}
289