1use std::fmt;
5use std::marker::PhantomData;
6
7use super::value::{DbValue, ToDbValue};
8
9pub struct Col<M, T> {
23 name: &'static str,
24 _p: PhantomData<fn() -> (M, T)>,
25}
26
27impl<M, T> Clone for Col<M, T> {
28 fn clone(&self) -> Self {
29 *self
30 }
31}
32
33impl<M, T> Copy for Col<M, T> {}
34
35impl<M, T> fmt::Debug for Col<M, T> {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 f.debug_tuple("Col").field(&self.name).finish()
38 }
39}
40
41impl<M, T> Col<M, T> {
42 pub const fn new(name: &'static str) -> Self {
44 Self {
45 name,
46 _p: PhantomData,
47 }
48 }
49
50 pub const fn name(&self) -> &'static str {
52 self.name
53 }
54}
55
56pub trait IntoColumn<M> {
61 fn column_name(&self) -> &str;
63}
64
65impl<M> IntoColumn<M> for &str {
66 fn column_name(&self) -> &str {
67 self
68 }
69}
70
71impl<M> IntoColumn<M> for &&str {
72 fn column_name(&self) -> &str {
73 self
74 }
75}
76
77impl<M> IntoColumn<M> for String {
78 fn column_name(&self) -> &str {
79 self
80 }
81}
82
83impl<M> IntoColumn<M> for &String {
84 fn column_name(&self) -> &str {
85 self
86 }
87}
88
89impl<M, T> IntoColumn<M> for Col<M, T> {
90 fn column_name(&self) -> &str {
91 self.name
92 }
93}
94
95pub trait Comparable<V> {}
100
101impl<T: ToDbValue> Comparable<T> for T {}
102impl<T: ToDbValue> Comparable<T> for Option<T> {}
103impl Comparable<&str> for String {}
104impl Comparable<&str> for Option<String> {}
105
106#[non_exhaustive]
109pub struct Condition<M> {
110 pub(super) kind: CondKind,
111 _p: PhantomData<fn() -> M>,
112}
113
114pub(super) enum CondKind {
115 Op(&'static str, &'static str, DbValue),
116 Null(&'static str, bool),
117 In(&'static str, Vec<DbValue>),
118}
119
120impl<M> fmt::Debug for Condition<M> {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.write_str("Condition")
123 }
124}
125
126impl<M> Condition<M> {
127 fn new(kind: CondKind) -> Self {
128 Self {
129 kind,
130 _p: PhantomData,
131 }
132 }
133}
134
135macro_rules! comparison {
136 ($($(#[$doc:meta])* $method:ident => $op:literal),* $(,)?) => {
137 $(
138 $(#[$doc])*
139 pub fn $method<V: ToDbValue>(self, value: V) -> Condition<M>
140 where
141 T: Comparable<V>,
142 {
143 Condition::new(CondKind::Op(self.name, $op, value.to_db_value()))
144 }
145 )*
146 };
147}
148
149impl<M, T> Col<M, T> {
150 comparison! {
151 eq => "=",
153 ne => "!=",
155 lt => "<",
157 lte => "<=",
159 gt => ">",
161 gte => ">=",
163 }
164
165 pub fn is_null(self) -> Condition<M> {
167 Condition::new(CondKind::Null(self.name, true))
168 }
169
170 pub fn is_not_null(self) -> Condition<M> {
172 Condition::new(CondKind::Null(self.name, false))
173 }
174
175 pub fn is_in<V: ToDbValue>(self, values: impl IntoIterator<Item = V>) -> Condition<M>
177 where
178 T: Comparable<V>,
179 {
180 let values = values.into_iter().map(|v| v.to_db_value()).collect();
181 Condition::new(CondKind::In(self.name, values))
182 }
183}
184
185impl<M> Col<M, String> {
186 pub fn like(self, pattern: &str) -> Condition<M> {
188 Condition::new(CondKind::Op(self.name, "like", pattern.to_db_value()))
189 }
190}
191
192impl<M> Col<M, Option<String>> {
193 pub fn like(self, pattern: &str) -> Condition<M> {
195 Condition::new(CondKind::Op(self.name, "like", pattern.to_db_value()))
196 }
197}