1
use partialdebug::placeholder::PartialDebug;
2
use proc_macro2::{Ident, TokenStream};
3
use quote::quote;
4
use std::convert::TryFrom;
5
use syn::{
6
    parse::{Parse, ParseBuffer},
7
    Attribute, Generics, ItemStruct, Visibility,
8
};
9

            
10
use super::entity_fields::EntityField;
11

            
12
/// Provides a convenient way of handling the data on any
13
/// `CanyonEntity` struct annotated with the macro `#[canyon_entity]`
14
#[derive(PartialDebug, Clone)]
15
pub struct CanyonEntity {
16
    pub struct_name: Ident,
17
    pub user_table_name: Option<String>,
18
    pub user_schema_name: Option<String>,
19
    pub vis: Visibility,
20
    pub generics: Generics,
21
    pub fields: Vec<EntityField>,
22
    pub attrs: Vec<Attribute>,
23
}
24

            
25
unsafe impl Send for CanyonEntity {}
26
unsafe impl Sync for CanyonEntity {}
27

            
28
impl CanyonEntity {
29
    /// Generates as many variants for the enum as fields has the type
30
    /// which this enum is related to, and that type it's the entity
31
    /// stored in [`CanyonEntity`]
32
    /// of the corresponding field
33
3
    pub fn get_fields_as_enum_variants(&self) -> Vec<TokenStream> {
34
3
        self.fields
35
            .iter()
36
19
            .map(|f| {
37
19
                let field_name = &f.name;
38
19
                quote! { #field_name }
39
19
            })
40
            .collect::<Vec<_>>()
41
3
    }
42

            
43
    /// Generates as many variants for the enum as fields has the type
44
    /// which this enum is related to.
45
    ///
46
    /// Makes a variant `#field_name(#ty)` where `#ty` it's a trait object
47
    /// of type [`canyon_crud::bounds::QueryParameter`]
48
3
    pub fn get_fields_as_enum_variants_with_value(&self) -> Vec<TokenStream> {
49
3
        self.fields
50
            .iter()
51
19
            .map(|f| {
52
19
                let field_name = &f.name;
53
19
                quote! { #field_name(&'a dyn canyon_sql::crud::bounds::QueryParameter<'a>) }
54
19
            })
55
            .collect::<Vec<_>>()
56
3
    }
57

            
58
3
    pub fn create_match_arm_for_get_variant_as_str(&self, enum_name: &Ident) -> Vec<TokenStream> {
59
6
        self.fields
60
            .iter()
61
22
            .map(|f| {
62
19
                let field_name = &f.name;
63
19
                let field_name_as_str = f.name.to_string();
64

            
65
19
                quote! {
66
                    #enum_name::#field_name => #field_name_as_str
67
                }
68
19
            })
69
            .collect::<Vec<_>>()
70
3
    }
71

            
72
    /// Generates an implementation of the match pattern to find whatever variant
73
    /// is being requested when the method `.field_name_as_str(self)` it's invoked over some
74
    /// instance that implements the `canyon_sql_root::crud::bounds::FieldIdentifier` trait
75
    pub fn create_match_arm_for_get_variant_as_string(
76
        &self,
77
        enum_name: &Ident,
78
    ) -> Vec<TokenStream> {
79
        self.fields
80
            .iter()
81
            .map(|f| {
82
                let field_name = &f.name;
83
                let field_name_as_string = f.name.to_string();
84

            
85
                quote! {
86
                    #enum_name::#field_name => #field_name_as_string.to_string()
87
                }
88
            })
89
            .collect::<Vec<_>>()
90
    }
91

            
92
    /// Generates an implementation of the match pattern to find whatever variant
93
    /// is being requested when the method `.value()` it's invoked over some
94
    /// instance that implements the `canyon_sql_root::crud::bounds::FieldValueIdentifier` trait
95
3
    pub fn create_match_arm_for_relate_fields_with_values(
96
        &self,
97
        enum_name: &Ident,
98
    ) -> Vec<TokenStream> {
99
6
        self.fields
100
            .iter()
101
22
            .map(|f| {
102
19
                let field_name = &f.name;
103
19
                let field_name_as_string = f.name.to_string();
104

            
105
19
                quote! {
106
                    #enum_name::#field_name(v) => (#field_name_as_string, v)
107
                }
108
19
            })
109
            .collect::<Vec<_>>()
110
3
    }
111

            
112
3
    pub fn get_attrs_as_token_stream(&self) -> Vec<TokenStream> {
113
3
        self.fields
114
            .iter()
115
19
            .map(|f| {
116
19
                let name = &f.name;
117
19
                let ty = &f.field_type;
118
19
                quote! { pub #name: #ty }
119
19
            })
120
            .collect::<Vec<_>>()
121
3
    }
122
}
123

            
124
impl Parse for CanyonEntity {
125
6
    fn parse(input: &ParseBuffer) -> syn::Result<Self> {
126
6
        let _struct = input.parse::<ItemStruct>()?;
127

            
128
        // Retrieve the struct fields
129
6
        let mut parsed_fields: Vec<EntityField> = Vec::new();
130
44
        for field in _struct.fields {
131
38
            let struct_attribute = EntityField::try_from(&field)?;
132
38
            parsed_fields.push(struct_attribute)
133
38
        }
134

            
135
6
        Ok(Self {
136
6
            struct_name: _struct.ident,
137
6
            user_table_name: None,
138
6
            user_schema_name: None,
139
6
            vis: _struct.vis,
140
6
            generics: _struct.generics,
141
6
            fields: parsed_fields,
142
6
            attrs: _struct.attrs,
143
        })
144
6
    }
145
}