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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

/// Possible operators
/// - = | != | < | <= | > | >= | LIKE | IN
/// - AND | OR
/// ## Example
/// ```
/// use fire_postgres::whr;
/// let a = &"val".to_string();
/// let b = &"val2".to_string();
/// let c: &Option<String> = &None;
/// let query = whr!(a AND "b" != b OR c);
/// 
/// assert_eq!(r#""a" = $1 AND "b" != $2 OR "c" IS NULL"#, query.sql().to_string().trim());
/// ```
#[macro_export]
macro_rules! whr {
	($($tt:tt)*) => ({
		let mut params = vec![];
		let mut sql = $crate::query::SqlBuilder::new();
		$crate::whr_comp!(sql, params, $($tt)*);
		$crate::query::Query::new(sql, params)
	})
}

#[macro_export]
macro_rules! whr_comp {
	($s:ident, $p:ident,) => ();
	($s:ident, $p:ident, LIMIT $value:tt $($tt:tt)*) => ({
		let v: usize = $value;
		$s.space_after(format!("LIMIT {}", v));
		$crate::whr_comp!($s, $p, $($tt)*);
	});
	($s:ident, $p:ident, $id:ident $($tt:tt)*) => (
		$crate::short_whr_comp!($s, $p, stringify!($id), "=", $id, $($tt)*);
	);
	($s:ident, $p:ident, $name:tt = $value:tt $($tt:tt)*) => (
		$crate::short_whr_comp!($s, $p, $name, "=", $value, $($tt)*);
	);
	($s:ident, $p:ident, $name:tt != $value:tt $($tt:tt)*) => (
		$crate::short_whr_comp!($s, $p, $name, "!=", $value, $($tt)*);
	);
	($s:ident, $p:ident, $name:tt < $value:tt $($tt:tt)*) => (
		$crate::short_whr_comp!($s, $p, $name, "<", $value, $($tt)*);
	);
	($s:ident, $p:ident, $name:tt <= $value:tt $($tt:tt)*) => (
		$crate::short_whr_comp!($s, $p, $name, "<=", $value, $($tt)*);
	);
	($s:ident, $p:ident, $name:tt > $value:tt $($tt:tt)*) => (
		$crate::short_whr_comp!($s, $p, $name, ">", $value, $($tt)*);
	);
	($s:ident, $p:ident, $name:tt >= $value:tt $($tt:tt)*) => (
		$crate::short_whr_comp!($s, $p, $name, ">=", $value, $($tt)*);
	);
	($s:ident, $p:ident, $name:tt LIKE $value:tt $($tt:tt)*) => (
		$crate::short_whr_comp!($s, $p, $name, "LIKE", $value, $($tt)*);
	);
	($s:ident, $p:ident, $name:tt ~ $value:tt $($tt:tt)*) => (
		$crate::short_whr_comp!($s, $p, $name, "LIKE", format!("%{}%", $value), $($tt)*);
	);
	($s:ident, $p:ident, $name:tt ~= $value:tt $($tt:tt)*) => (
		$crate::short_whr_comp!($s, $p, $name, "LIKE", format!("%{}", $value), $($tt)*);
	);
	($s:ident, $p:ident, $name:tt =~ $value:tt $($tt:tt)*) => (
		$crate::short_whr_comp!($s, $p, $name, "LIKE", format!("{}%", $value), $($tt)*);
	);
	($s:ident, $p:ident, $name:tt IN $value:tt $($tt:tt)*) => (
		$crate::short_whr_in_comp!($s, $p, $name, $value, $($tt)*);
	);
}

#[macro_export]
macro_rules! short_whr_comp {
	($s:ident, $p:ident, $name:expr, $symb:expr, $value:expr, $($tt:tt)*) => (
		let param = $crate::query::Param::new($name, $value);

		let mut cont = true;

		// todo: can this become a noop
		if param.maybe_null() && param.data().is_null() {
			match $symb {
				"=" => {
					$s.space_after(format!("\"{}\" IS NULL", $name));
					cont = false;
				},
				"!=" => {
					$s.space_after(format!("\"{}\" IS NOT NULL", $name));
					cont = false;
				},
				_ => {}
			}
		}

		if cont {
			$s.space_after(format!("\"{}\" {}", $name, $symb));
			$s.param();
			$p.push(param);
		}
		
		$crate::whr_log!($s, $p, $($tt)*);
	);
	// LIMIT
}

#[macro_export]
macro_rules! short_whr_in_comp {
	($s:ident, $p:ident, $name:expr, $value:expr, $($tt:tt)*) => (
		if $value.iter().len() > 0 {
			$s.no_space(format!("\"{}\" IN (", $name));
			let end = $value.iter().len() - 1;
			for (i, v) in $value.iter().enumerate() {
				$s.param();
				$p.push($crate::query::Param::new($name, v));
				if i != end {
					$s.space_after(",");
				}
			}
			$s.no_space(")");
		} else {
			$s.space_after(format!("\"{}\" IS NULL", $name));
		}
		$crate::whr_log!($s, $p, $($tt)*);
	);
}

#[macro_export]
macro_rules! whr_log {
	($s:ident, $p:ident, AND $($tt:tt)+) => (
		$s.space("AND");
		$crate::whr_comp!($s, $p, $($tt)+);
	);
	($s:ident, $p:ident, OR $($tt:tt)+) => (
		$s.space("OR");
		$crate::whr_comp!($s, $p, $($tt)+);
	);
	($s:ident, $p:ident, LIMIT $value:tt $($tt:tt)*) => (
		let v: usize = $value;
		$s.space(format!("LIMIT {}", v));
		$crate::whr_comp!($s, $p, $($tt)*);
	);
	($s:ident, $p:ident,) => ();
}

#[cfg(test)]
mod tests {
	use crate::UniqueId;

	#[test]
	fn test_limit() {
		let id = &UniqueId::new();
		let limit = 10;
		let query = whr!(id LIMIT limit);
		assert_eq!(query.sql.to_string().trim(), "\"id\" = $1 LIMIT 10");
	}
}