1
use attribute_derive::Attribute;
2
use manyhow::Result;
3
use proc_macro2::TokenStream;
4
use quote::quote;
5
use syn::punctuated::Punctuated;
6
use syn::token::Paren;
7
use syn::{DeriveInput, Ident, LitStr, Path, Type, TypeTuple};
8

            
9
use crate::core_path;
10

            
11
849
#[derive(Attribute)]
12
#[attribute(ident = view)]
13
struct ViewAttribute {
14
    #[attribute(example = "CollectionType")]
15
    collection: Type,
16
    #[attribute(example = "KeyType")]
17
    key: Type,
18
    #[attribute(example = "\"by-name\"")]
19
    name: Option<LitStr>,
20
    #[attribute(example = "ValueType")]
21
    value: Option<Type>,
22
    #[attribute(example = "bosaidb::core")]
23
    core: Option<Path>,
24
    #[attribute(example = "Format or None")]
25
    serialization: Option<Path>,
26
}
27

            
28
pub fn derive(
29
    DeriveInput {
30
        attrs,
31
        ident,
32
        generics,
33
        ..
34
    }: DeriveInput,
35
) -> Result {
36
    let ViewAttribute {
37
57
        collection,
38
57
        key,
39
57
        name,
40
57
        value,
41
57
        core,
42
57
        serialization,
43
57
    } = ViewAttribute::from_attributes(&attrs)?;
44

            
45
57
    let core = core.unwrap_or_else(core_path);
46
57

            
47
57
    let value = value.unwrap_or_else(|| {
48
4
        Type::Tuple(TypeTuple {
49
4
            paren_token: Paren::default(),
50
4
            elems: Punctuated::new(),
51
4
        })
52
57
    });
53
57

            
54
57
    let name = name
55
57
        .as_ref()
56
57
        .map_or_else(|| ident.to_string(), LitStr::value);
57
57

            
58
57
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
59

            
60
57
    let serialization = match serialization {
61
3
        Some(serialization) if serialization.is_ident("None") => TokenStream::new(),
62
1
        Some(serialization) => quote! {
63
1
            impl #impl_generics #core::schema::SerializedView for #ident #ty_generics #where_clause {
64
1
                type Format = #serialization;
65
1

            
66
1
                fn format() -> Self::Format {
67
1
                    #serialization::default()
68
1
                }
69
1
            }
70
1
        },
71
54
        None => quote! {
72
54
            impl #impl_generics #core::schema::DefaultViewSerialization for #ident #ty_generics #where_clause {}
73
54
        },
74
    };
75

            
76
57
    Ok(quote! {
77
57
        impl #impl_generics #core::schema::View for #ident #ty_generics #where_clause {
78
57
            type Collection = #collection;
79
57
            type Key = #key;
80
57
            type Value = #value;
81
57

            
82
57
            fn name(&self) -> #core::schema::Name {
83
57
                #core::schema::Name::new(#name)
84
57
            }
85
57
        }
86
57
        #serialization
87
57
    })
88
57
}
89

            
90
164
#[derive(Attribute)]
91
#[attribute(ident = view_schema)]
92
struct ViewSchemaAttribute {
93
    #[attribute(example = "ViewType")]
94
    view: Option<Type>,
95
    #[attribute(example = "KeyType<'doc>")]
96
    mapped_key: Option<Type>,
97
    #[attribute(example = "\"by-name\"")]
98
    version: Option<u64>,
99
    #[attribute(example = "Lazy")]
100
    policy: Option<Ident>,
101
    #[attribute(example = "bosaidb::core")]
102
    core: Option<Path>,
103
}
104

            
105
pub fn derive_schema(
106
    DeriveInput {
107
        attrs,
108
        ident,
109
        generics,
110
        ..
111
    }: DeriveInput,
112
) -> Result {
113
    let ViewSchemaAttribute {
114
51
        view,
115
51
        mapped_key,
116
51
        version,
117
51
        policy,
118
51
        core,
119
51
    } = ViewSchemaAttribute::from_attributes(&attrs)?;
120

            
121
51
    let core = core.unwrap_or_else(core_path);
122
51

            
123
51
    let view = view.map_or_else(|| quote!(Self), |ty| quote!(#ty));
124
51

            
125
51
    let mapped_key = mapped_key.map_or_else(
126
51
        || quote!(<Self as #core::schema::View>::Key),
127
51
        |ty| quote!(#ty),
128
51
    );
129
51

            
130
51
    let version = version.map(|version| {
131
10
        quote!(fn version(&self) -> u64 {
132
10
            #version
133
10
        })
134
51
    });
135
51

            
136
51
    let policy = policy.map(|policy| {
137
10
        quote!(fn update_policy(&self) -> #core::schema::view::ViewUpdatePolicy {
138
10
            #core::schema::view::ViewUpdatePolicy::#policy
139
10
        })
140
51
    });
141
51

            
142
51
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
143
51

            
144
51
    Ok(quote! {
145
51
        impl #impl_generics #core::schema::ViewSchema for #ident #ty_generics #where_clause {
146
51
            type View = #view;
147
51
            type MappedKey<'doc> = #mapped_key;
148
51

            
149
51
            #version
150
51
            #policy
151
51
        }
152
51
    })
153
51
}