1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
5
6use use_sql_column::SqlColumnRef;
7use use_sql_ident::SqlIdentifier;
8use use_sql_operator::{
9 SqlComparisonOperator, SqlLogicalOperator, SqlNullOperator, SqlOperator, SqlPatternOperator,
10};
11use use_sql_param::SqlParameter;
12use use_sql_value::SqlValue;
13
14#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
16pub struct SqlExpression {
17 kind: SqlExpressionKind,
18}
19
20impl SqlExpression {
21 #[must_use]
23 pub const fn identifier(identifier: SqlIdentifier) -> Self {
24 Self {
25 kind: SqlExpressionKind::Identifier(identifier),
26 }
27 }
28
29 #[must_use]
31 pub const fn column(column: SqlColumnRef) -> Self {
32 Self {
33 kind: SqlExpressionKind::Column(column),
34 }
35 }
36
37 #[must_use]
39 pub const fn value(value: SqlValue) -> Self {
40 Self {
41 kind: SqlExpressionKind::Value(value),
42 }
43 }
44
45 #[must_use]
47 pub const fn parameter(parameter: SqlParameter) -> Self {
48 Self {
49 kind: SqlExpressionKind::Parameter(parameter),
50 }
51 }
52
53 #[must_use]
55 pub fn binary(left: Self, operator: SqlOperator, right: Self) -> Self {
56 Self {
57 kind: SqlExpressionKind::Operator {
58 left: Box::new(left),
59 operator,
60 right: Box::new(right),
61 },
62 }
63 }
64
65 #[must_use]
67 pub fn predicate(predicate: SqlPredicate) -> Self {
68 Self {
69 kind: SqlExpressionKind::Predicate(Box::new(predicate)),
70 }
71 }
72
73 #[must_use]
75 pub const fn kind(&self) -> &SqlExpressionKind {
76 &self.kind
77 }
78}
79
80impl fmt::Display for SqlExpression {
81 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
82 match &self.kind {
83 SqlExpressionKind::Identifier(identifier) => identifier.fmt(formatter),
84 SqlExpressionKind::Column(column) => column.fmt(formatter),
85 SqlExpressionKind::Value(value) => value.fmt(formatter),
86 SqlExpressionKind::Parameter(parameter) => parameter.fmt(formatter),
87 SqlExpressionKind::Operator {
88 left,
89 operator,
90 right,
91 } => write!(formatter, "({left} {operator} {right})"),
92 SqlExpressionKind::Predicate(predicate) => predicate.fmt(formatter),
93 }
94 }
95}
96
97impl From<SqlIdentifier> for SqlExpression {
98 fn from(value: SqlIdentifier) -> Self {
99 Self::identifier(value)
100 }
101}
102
103impl From<SqlColumnRef> for SqlExpression {
104 fn from(value: SqlColumnRef) -> Self {
105 Self::column(value)
106 }
107}
108
109impl From<SqlValue> for SqlExpression {
110 fn from(value: SqlValue) -> Self {
111 Self::value(value)
112 }
113}
114
115impl From<SqlParameter> for SqlExpression {
116 fn from(value: SqlParameter) -> Self {
117 Self::parameter(value)
118 }
119}
120
121#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
123pub enum SqlExpressionKind {
124 Identifier(SqlIdentifier),
125 Column(SqlColumnRef),
126 Value(SqlValue),
127 Parameter(SqlParameter),
128 Operator {
129 left: Box<SqlExpression>,
130 operator: SqlOperator,
131 right: Box<SqlExpression>,
132 },
133 Predicate(Box<SqlPredicate>),
134}
135
136#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
138pub enum SqlPredicate {
139 Comparison {
140 left: Box<SqlExpression>,
141 operator: SqlComparisonOperator,
142 right: Box<SqlExpression>,
143 },
144 Null {
145 expression: Box<SqlExpression>,
146 operator: SqlNullOperator,
147 },
148 Pattern {
149 left: Box<SqlExpression>,
150 operator: SqlPatternOperator,
151 right: Box<SqlExpression>,
152 },
153 Logical {
154 left: Box<Self>,
155 operator: SqlLogicalOperator,
156 right: Box<Self>,
157 },
158 Not(Box<Self>),
159}
160
161impl SqlPredicate {
162 #[must_use]
164 pub fn comparison(
165 left: SqlExpression,
166 operator: SqlComparisonOperator,
167 right: SqlExpression,
168 ) -> Self {
169 Self::Comparison {
170 left: Box::new(left),
171 operator,
172 right: Box::new(right),
173 }
174 }
175
176 #[must_use]
178 pub fn null(expression: SqlExpression, operator: SqlNullOperator) -> Self {
179 Self::Null {
180 expression: Box::new(expression),
181 operator,
182 }
183 }
184
185 #[must_use]
187 pub fn pattern(
188 left: SqlExpression,
189 operator: SqlPatternOperator,
190 right: SqlExpression,
191 ) -> Self {
192 Self::Pattern {
193 left: Box::new(left),
194 operator,
195 right: Box::new(right),
196 }
197 }
198
199 #[must_use]
201 pub fn logical(left: Self, operator: SqlLogicalOperator, right: Self) -> Self {
202 Self::Logical {
203 left: Box::new(left),
204 operator,
205 right: Box::new(right),
206 }
207 }
208
209 #[must_use]
211 pub fn negate(predicate: Self) -> Self {
212 Self::Not(Box::new(predicate))
213 }
214}
215
216impl fmt::Display for SqlPredicate {
217 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
218 match self {
219 Self::Comparison {
220 left,
221 operator,
222 right,
223 } => write!(formatter, "{left} {operator} {right}"),
224 Self::Null {
225 expression,
226 operator,
227 } => write!(formatter, "{expression} {operator}"),
228 Self::Pattern {
229 left,
230 operator,
231 right,
232 } => write!(formatter, "{left} {operator} {right}"),
233 Self::Logical {
234 left,
235 operator,
236 right,
237 } => write!(formatter, "({left}) {operator} ({right})"),
238 Self::Not(predicate) => write!(formatter, "NOT ({predicate})"),
239 }
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::{SqlExpression, SqlPredicate};
246 use use_sql_column::{SqlColumnName, SqlColumnRef};
247 use use_sql_operator::{SqlComparisonOperator, SqlNullOperator};
248 use use_sql_param::SqlParameter;
249 use use_sql_value::SqlValue;
250
251 #[test]
252 fn renders_simple_expressions() -> Result<(), Box<dyn std::error::Error>> {
253 let column = SqlExpression::column(SqlColumnRef::new(SqlColumnName::new("id")?));
254 let parameter = SqlExpression::parameter("$1".parse::<SqlParameter>()?);
255 let predicate = SqlPredicate::comparison(column, SqlComparisonOperator::Equal, parameter);
256
257 assert_eq!(predicate.to_string(), "id = $1");
258 assert_eq!(
259 SqlExpression::value(SqlValue::string("Ada")).to_string(),
260 "'Ada'"
261 );
262 Ok(())
263 }
264
265 #[test]
266 fn renders_null_predicates() -> Result<(), Box<dyn std::error::Error>> {
267 let column = SqlExpression::column(SqlColumnRef::new(SqlColumnName::new("deleted_at")?));
268 let predicate = SqlPredicate::null(column, SqlNullOperator::IsNull);
269
270 assert_eq!(predicate.to_string(), "deleted_at IS NULL");
271 Ok(())
272 }
273}