1#![doc = include_str!("../README.md")]
2
3extern crate core;
4
5mod r#impl;
6
7use crate::r#impl::*;
8use quote::quote;
9use proc_macro::{TokenStream};
10use proc_macro2::Ident;
11use syn::{parse_macro_input, DeriveInput};
12use syn::Data::Struct;
13use syn::DataStruct;
14use syn::Fields::Named;
15use syn::FieldsNamed;
16
17#[proc_macro_derive(DynamoDb, attributes(partition,range,exclusion))]
18pub fn create_dynamodb_helper(item: TokenStream) -> TokenStream {
19 let ast = parse_macro_input!(item as DeriveInput);
20 let name = ast.ident;
21 let helper_name = format!("{name}Db");
22 let helper_ident = Ident::new(&helper_name, name.span());
23
24 let fields = match ast.data {
25 Struct(DataStruct { fields: Named(FieldsNamed { ref named, .. }), .. }) => named,
26 _ => unimplemented!("The DynamoDB procedural macro can only be used for structs"),
27 };
28
29 let exclusion_list = get_macro_attribute(&ast.attrs, EXCLUSION_ATTRIBUTE_NAME);
30 let exclusion_list_refs: Vec<&str> = exclusion_list.iter().map(|x| &**x).collect();
31
32 let (get_error, get_by_partition_error, batch_get_error, scan_error, parse_error) = generate_error_names(&helper_ident);
33 let errors = generate_helper_error(&helper_ident, &exclusion_list_refs);
34
35 let partition_key_ident_and_type = get_ident_and_type_of_field_annotated_with(fields, PARTITION_KEY_ATTRIBUTE_NAME)
36 .expect("You need to define a partition key for your DynamoDB struct! Place the field attribute `#[partition]` above the property that will serve as your key. Note that DynamoDB only supports strings, numbers and booleans as keys.");
37
38 let range_key_ident_and_type = get_ident_and_type_of_field_annotated_with(fields, RANGE_KEY_ATTRIBUTE_NAME);
39
40 let from_struct_for_hashmap = tokenstream_or_empty_if_no_put_methods(
41 from_struct_for_hashmap(&name, fields), &exclusion_list_refs
42 );
43
44 let try_from_hashmap_for_struct = tokenstream_or_empty_if_no_retrieval_methods(
45 try_from_hashmap_to_struct(&name, &parse_error, fields), &exclusion_list_refs,
46 );
47
48 let new = tokenstream_or_empty_if_exclusion(
49 new_method(&helper_ident), NEW_METHOD_NAME, &exclusion_list_refs,
50 );
51
52 let build = tokenstream_or_empty_if_exclusion(
53 build_method(&helper_ident), BUILD_METHOD_NAME, &exclusion_list_refs,
54 );
55
56 let gets = tokenstream_or_empty_if_exclusion(
57 get_methods(&name, &get_error, &get_by_partition_error, partition_key_ident_and_type, range_key_ident_and_type), GET_METHOD_NAME, &exclusion_list_refs
58 );
59
60 let batch_get = tokenstream_or_empty_if_exclusion(
61 batch_get(&name, &batch_get_error, partition_key_ident_and_type, range_key_ident_and_type), BATCH_GET_METHOD_NAME, &exclusion_list_refs
62 );
63
64 let create_table = tokenstream_or_empty_if_exclusion(
65 create_table_method(partition_key_ident_and_type, range_key_ident_and_type), CREATE_TABLE_METHOD_NAME, &exclusion_list_refs
66 );
67 let delete_table = tokenstream_or_empty_if_exclusion(
68 delete_table_method(), DELETE_TABLE_METHOD_NAME, &exclusion_list_refs
69 );
70 let put = tokenstream_or_empty_if_exclusion(
71 put_method(&name), PUT_METHOD_NAME, &exclusion_list_refs,
72 );
73 let batch_put = tokenstream_or_empty_if_exclusion(
74 batch_put_method(&name), BATCH_PUT_METHOD_NAME, &exclusion_list_refs,
75 );
76 let delete = tokenstream_or_empty_if_exclusion(
77 delete_method(&name, partition_key_ident_and_type, range_key_ident_and_type), DELETE_METHOD_NAME, &exclusion_list_refs,
78 );
79 let scan = tokenstream_or_empty_if_exclusion(
80 scan_method(&name, &scan_error), SCAN_METHOD_NAME, &exclusion_list_refs,
81 );
82
83 let public_version = quote! {
84 #from_struct_for_hashmap
85 #try_from_hashmap_for_struct
86
87 pub struct #helper_ident {
88 pub client: aws_sdk_dynamodb::Client,
89 pub table: String,
90 }
91
92 impl #helper_ident {
93 #new
94 #build
95
96 #create_table
97 #delete_table
98
99 #put
100 #gets
101 #batch_get
102 #batch_put
103 #delete
104 #scan
105 }
106
107 #errors
108 };
109
110 public_version.into()
111}