@@ -6,14 +6,24 @@ use syn::{
6
6
Ident , LitStr , Token ,
7
7
} ;
8
8
9
+ /// Represents a parsed attribute that appears in `#[dynomite(...)]`.
9
10
#[ derive( Clone ) ]
10
- pub ( crate ) struct Attr {
11
+ pub ( crate ) struct Attr < Kind > {
12
+ /// The identifier part of the attribute (e.g. `rename` in `#[dynomite(rename = "foo"`))
11
13
pub ( crate ) ident : Ident ,
12
- pub ( crate ) kind : AttrKind ,
14
+ /// More specific information about the metadata entry.
15
+ pub ( crate ) kind : Kind ,
13
16
}
14
17
18
+ /// Attribute that appears on record fields (struct fields and enum record variant fields)
19
+ pub ( crate ) type FieldAttr = Attr < FieldAttrKind > ;
20
+ /// Attribute that appears on the top level of an enum
21
+ pub ( crate ) type EnumAttr = Attr < EnumAttrKind > ;
22
+ /// Attribute that appears on enum varinats
23
+ pub ( crate ) type VariantAttr = Attr < VariantAttrKind > ;
24
+
15
25
#[ derive( Clone ) ]
16
- pub ( crate ) enum AttrKind {
26
+ pub ( crate ) enum FieldAttrKind {
17
27
/// Denotes field should be replaced with Default impl when absent in ddb
18
28
Default ,
19
29
/// Denotes field should be renamed to value of ListStr
@@ -26,51 +36,122 @@ pub(crate) enum AttrKind {
26
36
Flatten ,
27
37
}
28
38
29
- impl Attr {
30
- fn new (
31
- ident : Ident ,
32
- kind : AttrKind ,
33
- ) -> Self {
34
- Self { ident, kind }
39
+ impl DynomiteAttr for FieldAttrKind {
40
+ const KVS : Kvs < Self > = & [ ( "rename" , FieldAttrKind :: Rename ) ] ;
41
+ const KEYS : Keys < Self > = & [
42
+ ( "default" , FieldAttrKind :: Default ) ,
43
+ ( "partition_key" , FieldAttrKind :: PartitionKey ) ,
44
+ ( "sort_key" , FieldAttrKind :: SortKey ) ,
45
+ ( "flatten" , FieldAttrKind :: Flatten ) ,
46
+ ] ;
47
+ }
48
+
49
+ #[ derive( Clone ) ]
50
+ pub ( crate ) enum EnumAttrKind {
51
+ // FIXME: implement content attribute to support non-map values in enum variants
52
+ // (adjacently tagged enums: https://serde.rs/enum-representations.html#adjacently-tagged)
53
+ // Content(LitStr),
54
+ /// The name of the tag field for an internally-tagged enum
55
+ Tag ( LitStr ) ,
56
+ }
57
+
58
+ impl DynomiteAttr for EnumAttrKind {
59
+ const KVS : Kvs < Self > = & [ ( "tag" , EnumAttrKind :: Tag ) ] ;
60
+ }
61
+ #[ derive( Clone ) ]
62
+ pub ( crate ) enum VariantAttrKind {
63
+ // TODO: add default for enum variants?
64
+ Rename ( LitStr ) ,
65
+ }
66
+
67
+ impl DynomiteAttr for VariantAttrKind {
68
+ const KVS : Kvs < Self > = & [ ( "rename" , VariantAttrKind :: Rename ) ] ;
69
+ }
70
+
71
+ type Kvs < T > = & ' static [ ( & ' static str , fn ( syn:: LitStr ) -> T ) ] ;
72
+ type Keys < T > = & ' static [ ( & ' static str , T ) ] ;
73
+
74
+ /// Helper to ease defining `#[dynomite(key)` and `#[dynomite(key = "val")` attributes
75
+ pub ( crate ) trait DynomiteAttr : Clone + Sized + ' static {
76
+ /// List of `("attr_name", enum_variant_constructor)` to define attributes
77
+ /// that require a value string literal (e.g. `rename = "foo"`)
78
+ const KVS : Kvs < Self > = & [ ] ;
79
+ /// List of `("attr_name", enum_variant_value)` entires to define attributes
80
+ /// that should not have any value (e.g. `default` or `flatten`)
81
+ const KEYS : Keys < Self > = & [ ] ;
82
+ }
83
+
84
+ impl < A : DynomiteAttr > Parse for Attr < A > {
85
+ fn parse ( input : ParseStream ) -> syn:: Result < Self > {
86
+ let entry: MetadataEntry = input. parse ( ) ?;
87
+ let kind = entry
88
+ . try_attr_with_val ( A :: KVS )
89
+ . or_else ( || entry. try_attr_without_val ( A :: KEYS ) )
90
+ . unwrap_or_else ( || abort ! ( entry. key, "unexpected dynomite attribute: {}" , entry. key) ) ;
91
+ Ok ( Attr {
92
+ ident : entry. key ,
93
+ kind,
94
+ } )
95
+ }
96
+ }
97
+
98
+ struct MetadataEntry {
99
+ key : Ident ,
100
+ val : Option < LitStr > ,
101
+ }
102
+
103
+ impl MetadataEntry {
104
+ /// Attempt to map the parsed entry to an identifier-only attribute from the list
105
+ fn try_attr_without_val < T : Clone > (
106
+ & self ,
107
+ mappings : Keys < T > ,
108
+ ) -> Option < T > {
109
+ let Self { key, val } = self ;
110
+ let key_str = key. to_string ( ) ;
111
+ mappings
112
+ . iter ( )
113
+ . find ( |( key_pat, _) | * key_pat == key_str)
114
+ . map ( |( _, enum_val) | match val {
115
+ Some ( _) => abort ! ( key, "expected no value for dynomite attribute `{}`" , key) ,
116
+ None => enum_val. clone ( ) ,
117
+ } )
118
+ }
119
+
120
+ /// Attempt to map the parsed entry to a key-value attribute from the list
121
+ fn try_attr_with_val < T > (
122
+ & self ,
123
+ mappings : Kvs < T > ,
124
+ ) -> Option < T > {
125
+ let Self { key, val } = self ;
126
+ let key_str = key. to_string ( ) ;
127
+ mappings
128
+ . iter ( )
129
+ . find ( |( key_pat, _) | * key_pat == key_str)
130
+ . map ( |( _, to_enum) | match val {
131
+ Some ( it) => to_enum ( it. clone ( ) ) ,
132
+ None => abort ! (
133
+ key,
134
+ "expected a value for dynomite attribute: `{} = \" foo\" `" ,
135
+ key
136
+ ) ,
137
+ } )
35
138
}
36
139
}
37
140
38
- impl Parse for Attr {
141
+ impl Parse for MetadataEntry {
39
142
fn parse ( input : ParseStream ) -> syn:: Result < Self > {
40
- let name: Ident = input. parse ( ) ?;
41
- let name_str = name. to_string ( ) ;
42
- if input. peek ( Token ! [ =] ) {
43
- // `name = value` attributes.
44
- let assign = input. parse :: < Token ! [ =] > ( ) ?; // skip '='
45
- if input. peek ( LitStr ) {
46
- let lit: LitStr = input. parse ( ) ?;
47
- match & * name_str {
48
- "rename" => Ok ( Attr :: new ( name, AttrKind :: Rename ( lit) ) ) ,
49
- unsupported => abort ! {
50
- name,
51
- "unsupported dynomite {} attribute" ,
52
- unsupported
53
- } ,
54
- }
55
- } else {
56
- abort ! {
57
- assign,
58
- "expected `string literal` after `=`"
59
- } ;
60
- }
61
- } else if input. peek ( syn:: token:: Paren ) {
143
+ let key: Ident = input. parse ( ) ?;
144
+ if input. peek ( syn:: token:: Paren ) {
62
145
// `name(...)` attributes.
63
- abort ! ( name, "unexpected dynomite attribute: {}" , name_str) ;
64
- } else {
65
- // Attributes represented with a sole identifier.
66
- let kind = match name_str. as_ref ( ) {
67
- "default" => AttrKind :: Default ,
68
- "partition_key" => AttrKind :: PartitionKey ,
69
- "sort_key" => AttrKind :: SortKey ,
70
- "flatten" => AttrKind :: Flatten ,
71
- _ => abort ! ( name, "unexpected dynomite attribute: {}" , name_str) ,
72
- } ;
73
- Ok ( Attr :: new ( name, kind) )
146
+ abort ! ( key, "unexpected paren in dynomite attribute: {}" , key) ;
74
147
}
148
+ Ok ( Self {
149
+ key,
150
+ val : input
151
+ . parse :: < Token ! [ =] > ( )
152
+ . ok ( )
153
+ . map ( |_| input. parse ( ) )
154
+ . transpose ( ) ?,
155
+ } )
75
156
}
76
157
}
0 commit comments