odbc_futures_derive/
lib.rs1use inflector::Inflector;
2use quote::quote;
3use syn::Attribute;
4use synstructure::decl_derive;
5
6fn odbc_columns_derive(mut s: synstructure::Structure) -> proc_macro2::TokenStream {
7 const ODBC_STRING_UTF8: &'static str = "odbc_string_utf8";
8
9 let mut struct_use_utf8 = false;
10 let mut seq_col_number = 0u16;
11 let mut caseconv: Option<fn(&String) -> String> = None;
12
13 for struct_meta in s
14 .ast()
15 .attrs
16 .iter()
17 .map(Attribute::parse_meta)
18 .filter_map(Result::ok)
19 {
20 if let syn::Meta::Word(name) = &struct_meta {
21 let name = name.to_string();
22 match name.as_str() {
23 ODBC_STRING_UTF8 => struct_use_utf8 = true,
24 "odbc_columns_seq" => {
25 seq_col_number = 1;
26 }
27 "odbc_rename_camel_case" => caseconv = Some(Inflector::to_camel_case),
28 "odbc_rename_class_case" => caseconv = Some(Inflector::to_class_case),
29 "odbc_rename_kebab_case" => caseconv = Some(Inflector::to_kebab_case),
30 "odbc_rename_train_case" => caseconv = Some(Inflector::to_train_case),
31 "odbc_rename_screaming_snake_case" => {
32 caseconv = Some(Inflector::to_screaming_snake_case)
33 }
34 "odbc_rename_table_case" => caseconv = Some(Inflector::to_table_case),
35 "odbc_rename_sentence_case" => caseconv = Some(Inflector::to_sentence_case),
36 "odbc_rename_snake_case" => caseconv = Some(Inflector::to_snake_case),
37 "odbc_rename_pascal_case" => caseconv = Some(Inflector::to_pascal_case),
38 "odbc_rename_foreign_key" => caseconv = Some(Inflector::to_foreign_key),
39 "odbc_rename_pluralize" => caseconv = Some(Inflector::to_plural),
40 "odbc_rename_singularize" => caseconv = Some(Inflector::to_singular),
41 _ => {}
42 }
43 }
44 }
45
46 s.filter(|bi|{
47 for field_meta in bi.ast().attrs.iter().map(Attribute::parse_meta).filter_map(Result::ok) {
48 if let syn::Meta::Word(name) = &field_meta {
49 if name == "odbc_ignore" {
50 return false;
51 }
52 }
53 }
54 true
55 });
56
57 let body = s.bind_with(|_| synstructure::BindStyle::RefMut).each(|bi| {
58 let field = bi.ast();
59
60 let mut field_use_utf8 = false;
61 let mut field_force_use_utf16 = false;
62 let mut use_time2: Option<bool> = None;
63
64 let mut col_name = field.ident.as_ref().map(ToString::to_string);
65 if let Some(caseconv) = caseconv {
66 col_name = col_name.as_ref().map(caseconv);
67 }
68
69 let mut col_number: Option<u16> = None;
70 let mut visit_nested = false;
71
72 for field_meta in field
73 .attrs
74 .iter()
75 .map(Attribute::parse_meta)
76 .filter_map(Result::ok)
77 {
78 match &field_meta {
79 syn::Meta::Word(name) if name == ODBC_STRING_UTF8 => field_use_utf8 = true,
80 syn::Meta::Word(name) if name == "odbc_string_utf16" => field_force_use_utf16 = true,
81 syn::Meta::Word(name) if name == "odbc_nested" => {
82 visit_nested = true;
83 break;
84 }
85 syn::Meta::NameValue(name_value) if name_value.ident == "odbc_time" => {
86 match &name_value.lit {
87 syn::Lit::Str(name) if name.value() == "time" => use_time2 = Some(false),
88 syn::Lit::Str(name) if name.value() == "time2" => use_time2 = Some(true),
89 x => panic!("not a valid odbc_time name {:?}", x),
90 }
91 }
92 syn::Meta::NameValue(name_value) if name_value.ident == "odbc_col_name" => {
93 match &name_value.lit {
94 syn::Lit::Str(name) => col_name = Some(name.value()),
95 x => panic!("not a valid column name {:?}", x),
96 }
97 }
98 syn::Meta::NameValue(name_value) if name_value.ident == "odbc_col_number" => {
99 match &name_value.lit {
100 syn::Lit::Int(number) => {
101 let val = number.value();
102 assert!(val > 0, "column numbers start at 1");
103 assert!(val <= std::u16::MAX as u64);
104 col_number = Some(val as u16);
105 }
106 x => panic!("not a valid column number {:?}", x),
107 }
108 }
109 _ => {}
110 }
111 }
112
113 if visit_nested {
114 return quote! {
115 if let Some(poll) = #bi.visit(visitor) {
116 return Some(poll);
117 }
118 }
119 }
120
121 assert!(!field_force_use_utf16 || !field_use_utf8);
122
123 let ty = &field.ty;
124 let field_type: String = quote!(#ty).to_string();
125 let use_string_context = field_type == "String"
126 || field_type == "Option < String >"
127 || field_use_utf8
128 || field_force_use_utf16;
129
130 let context_type = if use_string_context {
131 if (struct_use_utf8 || field_use_utf8) && !field_force_use_utf16 {
132 quote!(())
133 } else {
134 quote!(Vec<u16>)
135 }
136 } else if let Some(time2) = use_time2 {
137 if time2 {
138 quote!(odbc_sys::SQL_SS_TIME2_STRUCT)
139 } else {
140 quote!(odbc_sys::SQL_TIME_STRUCT)
141 }
142 } else {
143 quote!(_)
144 };
145
146 let accept = quote! {
147 return Some( visitor.accept::< _, #context_type >(#bi) );
148 };
149
150 if let Some(col_number) = col_number {
151 quote! {
152 if visitor.col_number() == #col_number {
153 #accept
154 }
155 }
156 } else if seq_col_number > 0 {
157 let val = seq_col_number;
158 seq_col_number += 1;
159 quote! {
160 if visitor.col_number() == #val {
161 #accept
162 }
163 }
164 } else if let Some(col_name) = col_name {
165 quote! {
166 if visitor.col_name() == #col_name {
167 #accept
168 }
169 }
170 } else {
171 panic!("failed to setup binding for field {:?}", field)
172 }
173 });
174
175 let in_self = std::env::var("CARGO_PKG_NAME").unwrap() == "odbc-futures";
176
177 let trait_name = if in_self {
178 quote!(crate::SqlColumns)
179 } else {
180 quote!(odbc_futures::SqlColumns)
181 };
182 let crate_name = if in_self {
183 quote!(crate)
184 } else {
185 quote!(odbc_futures)
186 };
187
188 let mut stream = s.bound_impl(trait_name, quote! {
189 fn visit(&mut self, visitor: &mut #crate_name::SqlColumnVisitor) -> Option< #crate_name::SqlPoll > {
190 extern crate futures;
191 extern crate odbc_sys;
192
193 match *self { #body };
194 None
195 }
196 });
197 if in_self {
198 let string = stream.to_string().replace("extern crate crate ;", "");
199 use std::str::FromStr;
200 stream = proc_macro2::TokenStream::from_str(&string).unwrap();
201 }
202 stream
204}
205
206decl_derive!(
207 [Odbc, attributes(
208 odbc_string_utf8,
209 odbc_columns_seq,
210 odbc_rename_camel_case,
211 odbc_rename_class_case,
212 odbc_rename_kebab_case,
213 odbc_rename_train_case,
214 odbc_rename_screaming_snake_case,
215 odbc_rename_table_case,
216 odbc_rename_sentence_case,
217 odbc_rename_snake_case,
218 odbc_rename_pascal_case,
219 odbc_rename_foreign_key,
220 odbc_rename_pluralize,
221 odbc_rename_singularize,
222 odbc_string_utf16,
223 odbc_time,
224 odbc_nested,
225 odbc_ignore,
226 odbc_col_name,
227 odbc_col_number,
228 )] => odbc_columns_derive
229);