1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#![allow(unused_imports)]
#![forbid(unsafe_code)]

extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn;

#[macro_use]
mod macros;

mod attr;
mod derive;
mod errors;
mod internal;
mod parsers;

use parsers::derive_parser;
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields};

/// Derive macro for `GeekTable` trait.
///
/// This macro will generate the implementation of `GeekTable` trait for the given struct.
/// The struct must have named fields.
///
/// # Example
///
/// This macro generates a number of methods for the struct, including `table` and `table_name` methods.
///
/// ```rust
/// use geekorm::{GeekTable, PrimaryKeyInteger};
/// use geekorm::prelude::*;
///
/// #[derive(GeekTable)]
/// struct User {
///     id: PrimaryKeyInteger,
///     name: String,
///     age: i32,
///     occupation: String,
/// }
///
/// // This will get you the underlying table information.
/// let table = User::table();
/// assert_eq!(User::table_name(), "User");
/// ```
///
/// # Generate New Rows
///
/// When the `new` feature is enabled, the following methods are generated for the struct:
///
/// - `PrimaryKey<T>` fields are not generated
/// - `Option<T>` fields are not generated
///
/// ```rust
/// use geekorm::{GeekTable, PrimaryKeyInteger};
/// use geekorm::prelude::*;
///
/// #[derive(GeekTable)]
/// struct User {
///     id: PrimaryKeyInteger,
///     name: String,
///     age: i32,
///     occupation: String,
///     country: Option<String>,
/// }
///
/// let user = User::new(
///     String::from("geekmasher"),
///     42,
///     String::from("Software Developer")
/// );
///
/// ```
///
/// # Generated Query Methods
///
/// The following methods are generated for the struct:
///
/// ```rust
/// use geekorm::{GeekTable, PrimaryKeyInteger};
/// use geekorm::prelude::*;
///
/// #[derive(GeekTable)]
/// struct User {
///     id: PrimaryKeyInteger,
///     name: String,
///     age: i32,
///     occupation: String,
/// }
///
/// // Create a new table query
/// let create = User::create().build()
///     .expect("Failed to build CREATE TABLE query");
///
/// // Select data from the table
/// let select = User::select()
///     .where_eq("name", "geekmasher")
///     .build()
///     .expect("Failed to build SELECT query");
/// ```
///
/// # Generated Helper Methods
///
/// When the `helpers` feature is enabled, the following helper methods are generated for the struct:
///
/// Note: This is a very experimental feature and might change in the future.
///
/// ```rust
/// use geekorm::{GeekTable, PrimaryKeyInteger};
/// use geekorm::prelude::*;
///
/// #[derive(GeekTable)]
/// struct User {
///     id: PrimaryKeyInteger,
///     name: String,
///     age: i32,
///     occupation: String,
/// }
///
/// // Select by column helper function
/// let user = User::select_by_name("geekmasher");
/// # assert_eq!(user.query, String::from("SELECT id, name, age, occupation FROM User WHERE name = ?;"));
/// let user = User::select_by_age(69);
/// # assert_eq!(user.query, String::from("SELECT id, name, age, occupation FROM User WHERE age = ?;"));
/// let user = User::select_by_occupation("Software Developer");
/// # assert_eq!(user.query, String::from("SELECT id, name, age, occupation FROM User WHERE occupation = ?;"));
/// ```
#[proc_macro_derive(GeekTable, attributes(geekorm))]
pub fn table_derive(input: TokenStream) -> TokenStream {
    let ast: DeriveInput = parse_macro_input!(input as DeriveInput);

    derive_parser(&ast).unwrap().into()
}