1use serde::{Deserialize, Serialize};
2use std::fmt::Display;
3
4use crate::{Columns, QueryBuilder, ToSqlite, Values};
5
6#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8pub struct Table {
9 pub name: String,
11 pub columns: Columns,
13 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub database: Option<String>,
18}
19
20impl Table {
21 pub fn is_valid_column(&self, column: &str) -> bool {
23 if let Some((table, column)) = column.split_once('.') {
24 if table != self.name {
25 return false;
26 }
27 self.columns.is_valid_column(column)
28 } else {
29 self.columns.is_valid_column(column)
30 }
31 }
32
33 pub fn get_primary_key(&self) -> String {
35 self.columns
36 .columns
37 .iter()
38 .find(|col| col.column_type.is_primary_key())
39 .map(|col| col.name.clone())
40 .unwrap_or_else(|| String::from("id"))
41 }
42
43 pub fn get_foreign_key(&self, table_name: String) -> &crate::Column {
45 for column in self.columns.get_foreign_keys() {
46 if column.column_type.is_foreign_key_table(&table_name) {
47 return column;
48 }
49 }
50 panic!("No foreign key found for column: {}", table_name);
51 }
52
53 pub fn get_fullname(&self, column: &str) -> Result<String, crate::Error> {
55 let column = self.columns.get(column).ok_or_else(|| {
56 crate::Error::ColumnNotFound(self.name.to_string(), column.to_string())
57 })?;
58 let name = if column.alias.is_empty() {
59 column.name.clone()
60 } else {
61 column.alias.clone()
62 };
63 Ok(format!("{}.{}", self.name, name))
64 }
65
66 pub fn get_dependencies(&self) -> Vec<String> {
70 let mut dependencies = Vec::new();
71 for column in &self.columns.columns {
72 if let Some(ftable) = column.column_type.foreign_key_table_name() {
73 dependencies.push(ftable);
74 }
75 }
76 dependencies
77 }
78}
79
80#[cfg(feature = "migrations")]
82impl quote::ToTokens for Table {
83 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
84 let name = &self.name;
85 let columns = &self.columns;
86 let database = self
87 .database
88 .clone()
89 .unwrap_or_else(|| "Database".to_string());
90
91 tokens.extend(quote::quote! {
92 geekorm::Table {
93 name: String::from(#name),
94 columns: #columns,
95 database: Some(String::from(#database)),
96 }
97 });
98 }
99}
100
101impl ToSqlite for Table {
102 fn on_create(&self, query: &QueryBuilder) -> Result<String, crate::Error> {
103 Ok(format!(
104 "CREATE TABLE IF NOT EXISTS {} {};",
105 self.name,
106 self.columns.on_create(query)?
107 ))
108 }
109
110 fn on_select(&self, qb: &QueryBuilder) -> Result<String, crate::Error> {
111 let mut full_query = String::new();
112
113 let columns = self.columns.on_select(qb);
115
116 if let Ok(ref columns) = columns {
117 if qb.count {
118 full_query = String::from("SELECT COUNT(1)");
120 } else {
121 let mut select_columns: Vec<String> = Vec::new();
123
124 let scolumns: Vec<String> = if !qb.columns.is_empty() {
125 qb.columns.clone()
126 } else {
127 self.columns
128 .columns
129 .iter()
130 .filter(|col| !col.skip)
131 .map(|col| col.name.clone())
132 .collect()
133 };
134
135 for column in scolumns {
136 if qb.joins.is_empty() {
138 select_columns.push(column);
140 } else {
141 if column.contains('.') {
143 select_columns.push(column);
145 } else {
146 let fullname = qb.table.get_fullname(&column)?;
148 select_columns.push(fullname);
149 }
150 }
151 }
152 full_query = format!("SELECT {}", select_columns.join(", "));
153 }
154
155 full_query.push_str(" FROM ");
157 full_query.push_str(&self.name);
158
159 if !qb.joins.is_empty() {
161 full_query.push(' ');
162 full_query.push_str(qb.joins.on_select(qb)?.as_str());
163 }
164
165 if !columns.is_empty() {
167 full_query.push(' ');
168 full_query.push_str(columns);
169 }
170
171 if let Some(limit) = qb.limit {
173 full_query.push_str(" LIMIT ");
175 full_query.push_str(&limit.to_string());
176 if let Some(offset) = qb.offset {
177 full_query.push_str(" OFFSET ");
178 full_query.push_str(&offset.to_string());
179 }
180 }
181
182 full_query = full_query.trim().to_string();
184 full_query.push(';');
185 }
186 Ok(full_query)
187 }
188
189 fn on_insert(&self, query: &QueryBuilder) -> Result<(String, Values), crate::Error> {
190 let mut full_query = format!("INSERT INTO {} ", self.name);
191
192 let mut columns: Vec<String> = Vec::new();
193 let mut values: Vec<String> = Vec::new();
194 let mut parameters = Values::new();
195
196 for (cname, value) in query.values.values.iter() {
197 let column = query.table.columns.get(cname.as_str()).unwrap();
198
199 let mut column_name = column.name.clone();
201 if !column.alias.is_empty() {
202 column_name = column.alias.to_string();
203 }
204
205 if column.column_type.is_auto_increment() {
207 continue;
208 }
209
210 columns.push(column_name.clone());
211
212 match value {
214 crate::Value::Identifier(_) | crate::Value::Text(_) | crate::Value::Json(_) => {
215 values.push(String::from("?"));
218 parameters.push(column_name, value.clone());
219 }
220 crate::Value::Blob(value) => {
221 values.push(String::from("?"));
223 parameters.push(column_name, value.clone());
224 }
225 crate::Value::Integer(value) => values.push(value.to_string()),
226 crate::Value::Boolean(value) => values.push(value.to_string()),
227 crate::Value::Null => values.push("NULL".to_string()),
228 }
229 }
230
231 full_query.push('(');
233 full_query.push_str(&columns.join(", "));
234 full_query.push(')');
235
236 full_query.push_str(" VALUES (");
238 full_query.push_str(&values.join(", "));
239 full_query.push(')');
240 full_query.push(';');
241
242 Ok((full_query, parameters))
243 }
244
245 fn on_update(&self, query: &QueryBuilder) -> Result<(String, Values), crate::Error> {
246 let mut full_query = format!("UPDATE {} SET ", self.name);
247
248 let mut columns: Vec<String> = Vec::new();
249 let mut parameters = Values::new();
250
251 for (cname, value) in query.values.values.iter() {
252 let column = query.table.columns.get(cname.as_str()).unwrap();
253
254 if column.column_type.is_primary_key() || cname == "id" {
256 continue;
257 }
258 let mut column_name = column.name.clone();
260 if !column.alias.is_empty() {
261 column_name = column.alias.to_string();
262 }
263
264 match value {
266 crate::Value::Identifier(_)
267 | crate::Value::Text(_)
268 | crate::Value::Blob(_)
269 | crate::Value::Json(_) => {
270 columns.push(format!("{} = ?", column_name));
273 parameters.push(column_name, value.clone());
274 }
275 crate::Value::Integer(value) => {
276 columns.push(format!("{} = {}", column_name, value))
277 }
278 crate::Value::Boolean(value) => {
279 columns.push(format!("{} = {}", column_name, value))
280 }
281 crate::Value::Null => columns.push(format!("{} = NULL", column_name)),
282 }
283 }
284
285 full_query.push_str(&columns.join(", "));
287
288 let primary_key_name = query.table.get_primary_key();
291 let primary_key = query.values.get(&primary_key_name).unwrap();
292 let where_clause = format!(" WHERE {} = {}", primary_key_name, primary_key);
293 full_query.push_str(&where_clause);
294 full_query.push(';');
295
296 Ok((full_query, parameters))
297 }
298
299 fn on_delete(&self, query: &QueryBuilder) -> Result<(String, Values), crate::Error> {
303 let mut full_query = format!("DELETE FROM {}", self.name);
304 let mut parameters = Values::new();
305
306 let primary_key_name = self.get_primary_key();
308 let primary_key = query.values.get(&primary_key_name).unwrap();
309
310 parameters.push(primary_key_name.to_string(), primary_key.clone());
311
312 full_query.push_str(&format!(" WHERE {} = ?;", primary_key_name));
313
314 Ok((full_query, parameters))
315 }
316
317 #[cfg(feature = "migrations")]
318 fn on_alter(&self, query: &crate::AlterQuery) -> Result<String, crate::Error> {
319 for column in &self.columns.columns {
320 if column.name == query.column {
321 return column.clone().on_alter(query);
322 }
323 }
324
325 Err(crate::Error::ColumnNotFound(
326 self.name.clone(),
327 query.column.clone(),
328 ))
329 }
330}
331
332impl Display for Table {
333 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
334 write!(f, "Table('{}')", self.name)
335 }
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341
342 fn table() -> Table {
343 use crate::{Column, ColumnType, ColumnTypeOptions};
344
345 Table {
346 name: "Test".to_string(),
347 database: None,
348 columns: vec![
349 Column::new(
350 "id".to_string(),
351 ColumnType::Integer(ColumnTypeOptions::primary_key()),
352 ),
353 Column::new(
354 "name".to_string(),
355 ColumnType::Text(ColumnTypeOptions::default()),
356 ),
357 ]
358 .into(),
359 }
360 }
361
362 #[test]
363 fn test_table_to_sql() {
364 let table = table();
365
366 let query = crate::QueryBuilder::select().table(table.clone());
367 assert_eq!(
369 table.on_create(&query).unwrap(),
370 "CREATE TABLE IF NOT EXISTS Test (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);"
371 );
372 assert_eq!(
373 table.on_select(&query).unwrap(),
374 "SELECT id, name FROM Test;"
375 );
376
377 let query = crate::QueryBuilder::select()
378 .table(table.clone())
379 .where_eq("name", "this");
380 assert_eq!(
381 table.on_select(&query).unwrap(),
382 "SELECT id, name FROM Test WHERE name = ?;"
383 );
384 }
385
386 #[test]
387 fn test_count() {
388 let table = table();
389
390 let query = crate::QueryBuilder::select().table(table.clone()).count();
391 assert_eq!(
392 table.on_select(&query).unwrap(),
393 "SELECT COUNT(1) FROM Test;"
394 );
395
396 let query = crate::QueryBuilder::select()
397 .table(table.clone())
398 .count()
399 .where_eq("name", "this");
400 assert_eq!(
401 table.on_select(&query).unwrap(),
402 "SELECT COUNT(1) FROM Test WHERE name = ?;"
403 );
404
405 let query = crate::QueryBuilder::select()
406 .table(table.clone())
407 .count()
408 .where_ne("name", "this");
409 assert_eq!(
410 table.on_select(&query).unwrap(),
411 "SELECT COUNT(1) FROM Test WHERE name != ?;"
412 );
413 }
414
415 #[test]
416 fn test_row_delete() {
417 let table = table();
418
419 let query = crate::QueryBuilder::delete()
420 .table(table.clone())
421 .where_eq("id", 1);
422 let (delete_query, _) = table.on_delete(&query).unwrap();
423
424 assert_eq!(delete_query, "DELETE FROM Test WHERE id = ?;");
425 }
426
427 #[test]
428 fn test_is_valid_column() {
429 let table = table();
430
431 assert!(table.is_valid_column("id"));
432 assert!(table.is_valid_column("name"));
433 assert!(!table.is_valid_column("name2"));
434 assert!(table.is_valid_column("Test.name"));
436 assert!(!table.is_valid_column("Tests.name"));
437 }
438}