1mod analyze;
2mod expand;
3mod from_glue_value;
4mod into_glue_expr;
5
6use proc_macro::TokenStream;
7use proc_macro2::Span;
8use quote::{quote as q, ToTokens};
9use syn::{
10 parse_macro_input, parse_quote, Data, DataStruct, DeriveInput, Expr, Field, Fields, Ident, Type,
11};
12
13pub(crate) use gluesql_core::ast::DataType as SqlType;
14use SqlType::*;
15
16#[proc_macro_derive(Table, attributes(pkey_column, unique_column))]
18pub fn table_derive(input: TokenStream) -> TokenStream {
19 let ast = parse_macro_input!(input as DeriveInput);
20 let struct_ident = ast.ident;
21 let table_name = struct_ident.to_string() + "s";
22
23 let fields = match ast.data {
25 Data::Struct(DataStruct {
26 fields: Fields::Named(it),
27 ..
28 }) => it,
29 _ => panic!("Expected a `struct` with named fields"),
30 };
31
32 let mut columns: Vec<Column> = fields.named.into_iter().map(analyze::from_field).collect();
34
35 match columns.iter().filter(|c| c.pkey).count() {
37 0 => {
38 columns[0].pkey = true;
39 columns[0].unique = true;
40 }
41 1 => {}
42 _ => panic!("Table macro doesn't support more than one pkey at the moment"),
43 };
44
45 TokenStream::from(expand::impl_table(struct_ident, table_name, columns))
47}
48
49struct Column {
50 field_name: Ident,
51 field_name_str: String,
52 full_type: Type,
53 full_type_str: String,
54 inner_type: Type,
56 sql_type: SqlType,
58 pkey: bool,
60 optional: bool,
62 list: bool,
64 unique: bool,
66 serialized: bool,
68}
69
70impl Column {
71 fn from_row_transform(&self) -> FromRowTransform {
72 match self.sql_type {
73 Uuid => FromRowTransform::UuidFromU128,
74 _ if self.serialized => FromRowTransform::Deserialize,
75 _ => FromRowTransform::None,
76 }
77 }
78
79 fn value_variant(&self) -> &str {
80 match self.sql_type {
81 Uuid => "Uuid",
82 Text => "Str",
83 Timestamp => "Timestamp",
84 Boolean => "Bool",
85 Uint128 => "U128",
86 Uint64 => "U64",
87 Uint32 => "U32",
88 Uint16 => "U16",
89 Uint8 => "U8",
90 Int128 => "I128",
91 Int => "I64",
92 Int32 => "I32",
93 Int16 => "I16",
94 Int8 => "I8",
95 Float32 => "F32",
96 Float => "F64",
97 _ => "Str",
98 }
99 }
100}
101
102enum FromRowTransform {
103 UuidFromU128,
104 Deserialize,
105 None,
106}
107
108fn ident(name: &str) -> Ident {
109 Ident::new(name, Span::call_site())
110}
111
112trait TypeProps {
113 fn impl_into_exprnode(&self) -> bool;
114 fn int_or_smaller(&self) -> bool;
115 fn integer(&self) -> bool;
116 fn numeric(&self) -> bool;
117 fn comparable(&self) -> bool;
118 fn quoted(&self) -> bool;
119}
120
121impl TypeProps for SqlType {
122 fn impl_into_exprnode(&self) -> bool {
123 matches!(self, Boolean | Int)
124 }
125 fn int_or_smaller(&self) -> bool {
126 matches!(self, Uint32 | Uint16 | Uint8 | Int | Int32 | Int16 | Int8)
127 }
128 fn integer(&self) -> bool {
129 self.int_or_smaller() || matches!(self, Uint128 | Uint64 | Int128)
130 }
131 fn numeric(&self) -> bool {
132 self.integer() || matches!(self, Float | Float32)
133 }
134 fn comparable(&self) -> bool {
135 self.numeric() || matches!(self, Timestamp | Date | Time)
136 }
137 fn quoted(&self) -> bool {
138 !self.impl_into_exprnode() && !self.numeric()
139 }
140}
141
142impl TypeProps for Column {
143 fn impl_into_exprnode(&self) -> bool {
144 self.sql_type.impl_into_exprnode()
145 }
146 fn int_or_smaller(&self) -> bool {
147 self.sql_type.int_or_smaller()
148 }
149 fn integer(&self) -> bool {
150 self.sql_type.integer()
151 }
152 fn numeric(&self) -> bool {
153 self.sql_type.numeric()
154 }
155 fn comparable(&self) -> bool {
156 self.sql_type.comparable()
157 }
158 fn quoted(&self) -> bool {
159 self.sql_type.quoted()
160 }
161}
162
163fn column_schema(col: &Column) -> proc_macro2::TokenStream {
164 let Column {
165 field_name_str,
166 full_type_str,
167 sql_type,
168 pkey,
169 unique,
170 list,
171 optional,
172 serialized,
173 ..
174 } = col;
175 let numeric = sql_type.numeric();
176 let comparable = sql_type.comparable();
177 let sql_type = sql_type.to_string();
178 q! {
179 ColumnSchema {
180 name: #field_name_str,
181 rust_type: #full_type_str,
182 sql_type: #sql_type,
183 unique: #unique,
184 pkey: #pkey,
185 list: #list,
186 optional: #optional,
187 serialized: #serialized,
188 numeric: #numeric,
189 comparable: #comparable,
190 }
191 }
192}