@@ -10,26 +10,31 @@ use syn::{
10
10
#[ derive( Debug ) ]
11
11
struct WriteableField {
12
12
ident : Ident ,
13
+ is_time : bool ,
13
14
is_tag : bool ,
14
15
is_ignore : bool ,
15
16
}
16
17
17
18
mod kw {
18
19
use syn:: custom_keyword;
19
20
21
+ custom_keyword ! ( time) ;
20
22
custom_keyword ! ( tag) ;
21
23
custom_keyword ! ( ignore) ;
22
24
}
23
25
24
26
enum FieldAttr {
27
+ Time ( kw:: time ) ,
25
28
Tag ( kw:: tag ) ,
26
29
Ignore ( kw:: ignore ) ,
27
30
}
28
31
29
32
impl Parse for FieldAttr {
30
33
fn parse ( input : ParseStream < ' _ > ) -> syn:: Result < Self > {
31
34
let lookahead = input. lookahead1 ( ) ;
32
- if lookahead. peek ( kw:: tag) {
35
+ if lookahead. peek ( kw:: time) {
36
+ Ok ( Self :: Time ( input. parse ( ) ?) )
37
+ } else if lookahead. peek ( kw:: tag) {
33
38
Ok ( Self :: Tag ( input. parse ( ) ?) )
34
39
} else if lookahead. peek ( kw:: ignore) {
35
40
Ok ( Self :: Ignore ( input. parse ( ) ?) )
@@ -52,6 +57,7 @@ impl TryFrom<Field> for WriteableField {
52
57
53
58
fn try_from ( field : Field ) -> syn:: Result < WriteableField > {
54
59
let ident = field. ident . expect ( "fields without ident are not supported" ) ;
60
+ let mut has_time_attr = false ;
55
61
let mut is_tag = false ;
56
62
let mut is_ignore = false ;
57
63
@@ -60,6 +66,7 @@ impl TryFrom<Field> for WriteableField {
60
66
Meta :: List ( list) if list. path . is_ident ( "influxdb" ) => {
61
67
for attr in syn:: parse2 :: < FieldAttrs > ( list. tokens ) ?. 0 {
62
68
match attr {
69
+ FieldAttr :: Time ( _) => has_time_attr = true ,
63
70
FieldAttr :: Tag ( _) => is_tag = true ,
64
71
FieldAttr :: Ignore ( _) => is_ignore = true ,
65
72
}
@@ -69,8 +76,23 @@ impl TryFrom<Field> for WriteableField {
69
76
}
70
77
}
71
78
79
+ if [ has_time_attr, is_tag, is_ignore]
80
+ . iter ( )
81
+ . filter ( |& & b| b)
82
+ . count ( )
83
+ > 1
84
+ {
85
+ panic ! ( "only one of time, tag, or ignore can be used" ) ;
86
+ }
87
+
88
+ // A field is considered a time field if:
89
+ // 1. It has the #[influxdb(time)] attribute, OR
90
+ // 2. It's named "time" and doesn't have #[influxdb(ignore)]
91
+ let is_time = has_time_attr || ( ident == "time" && !is_ignore) ;
92
+
72
93
Ok ( WriteableField {
73
94
ident,
95
+ is_time,
74
96
is_tag,
75
97
is_ignore,
76
98
} )
@@ -97,39 +119,52 @@ pub fn expand_writeable(input: DeriveInput) -> syn::Result<TokenStream> {
97
119
}
98
120
} ;
99
121
100
- let time_field = format_ident ! ( "time" ) ;
101
- let time_field_str = time_field. to_string ( ) ;
102
- #[ allow( clippy:: cmp_owned) ] // that's not how idents work clippy
103
- let fields = match fields {
122
+ let writeable_fields: Vec < WriteableField > = match fields {
104
123
Fields :: Named ( fields) => fields
105
124
. named
106
125
. into_iter ( )
107
- . filter_map ( |f| {
108
- WriteableField :: try_from ( f)
109
- . map ( |wf| {
110
- if !wf. is_ignore && wf. ident . to_string ( ) != time_field_str {
111
- let ident = wf. ident ;
112
- Some ( match wf. is_tag {
113
- true => quote ! ( query. add_tag( stringify!( #ident) , self . #ident) ) ,
114
- false => quote ! ( query. add_field( stringify!( #ident) , self . #ident) ) ,
115
- } )
116
- } else {
117
- None
118
- }
119
- } )
120
- . transpose ( )
121
- } )
126
+ . map ( WriteableField :: try_from)
122
127
. collect :: < syn:: Result < Vec < _ > > > ( ) ?,
123
- _ => panic ! ( "a struct without named fields is not supported" ) ,
128
+ _ => panic ! ( "A struct without named fields is not supported! " ) ,
124
129
} ;
125
130
131
+ // Find the time field
132
+ let mut time_field = None ;
133
+ for wf in & writeable_fields {
134
+ if wf. is_time {
135
+ if time_field. is_some ( ) {
136
+ panic ! ( "multiple time fields found!" ) ;
137
+ }
138
+ time_field = Some ( wf. ident . clone ( ) ) ;
139
+ }
140
+ }
141
+
142
+ // There must be exactly one time field
143
+ let time_field = time_field. expect ( "no time field found" ) ;
144
+
145
+ // Generate field assignments (excluding time and ignored fields)
146
+ let field_assignments = writeable_fields
147
+ . into_iter ( )
148
+ . filter_map ( |wf| {
149
+ if wf. is_ignore || wf. is_time {
150
+ None
151
+ } else {
152
+ let ident = wf. ident ;
153
+ Some ( match wf. is_tag {
154
+ true => quote ! ( query. add_tag( stringify!( #ident) , self . #ident) ) ,
155
+ false => quote ! ( query. add_field( stringify!( #ident) , self . #ident) ) ,
156
+ } )
157
+ }
158
+ } )
159
+ . collect :: < Vec < _ > > ( ) ;
160
+
126
161
Ok ( quote ! {
127
162
impl #impl_generics :: influxdb:: InfluxDbWriteable for #ident #ty_generics #where_clause {
128
163
fn into_query<I : Into <String >>( self , name: I ) -> :: influxdb:: WriteQuery {
129
164
let timestamp: :: influxdb:: Timestamp = self . #time_field. into( ) ;
130
165
let mut query = timestamp. into_query( name) ;
131
166
#(
132
- query = #fields ;
167
+ query = #field_assignments ;
133
168
) *
134
169
query
135
170
}
0 commit comments