@@ -6,14 +6,24 @@ use syn::{
66 Ident , LitStr , Token ,
77} ;
88
9+ /// Represents a parsed attribute that appears in `#[dynomite(...)]`.
910#[ 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"`))
1113 pub ( crate ) ident : Ident ,
12- pub ( crate ) kind : AttrKind ,
14+ /// More specific information about the metadata entry.
15+ pub ( crate ) kind : Kind ,
1316}
1417
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+
1525#[ derive( Clone ) ]
16- pub ( crate ) enum AttrKind {
26+ pub ( crate ) enum FieldAttrKind {
1727 /// Denotes field should be replaced with Default impl when absent in ddb
1828 Default ,
1929 /// Denotes field should be renamed to value of ListStr
@@ -26,51 +36,122 @@ pub(crate) enum AttrKind {
2636 Flatten ,
2737}
2838
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+ } )
35138 }
36139}
37140
38- impl Parse for Attr {
141+ impl Parse for MetadataEntry {
39142 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 ) {
62145 // `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) ;
74147 }
148+ Ok ( Self {
149+ key,
150+ val : input
151+ . parse :: < Token ! [ =] > ( )
152+ . ok ( )
153+ . map ( |_| input. parse ( ) )
154+ . transpose ( ) ?,
155+ } )
75156 }
76157}
0 commit comments