1use {
2 crate::{
3 ast::Expr,
4 data::{Interval, Point, Value},
5 },
6 chrono::{NaiveDate, NaiveDateTime, NaiveTime},
7 rust_decimal::Decimal,
8 std::net::IpAddr,
9 uuid::Uuid,
10};
11
12#[derive(Debug, Clone)]
13pub struct ParamLiteral(Value);
14
15impl ParamLiteral {
16 #[must_use]
17 pub const fn null() -> Self {
18 Self(Value::Null)
19 }
20
21 #[must_use]
22 pub fn into_expr(self) -> Expr {
23 Expr::Value(self.0)
24 }
25}
26
27pub trait IntoParamLiteral {
28 fn into_param_literal(self) -> ParamLiteral;
30}
31
32impl IntoParamLiteral for ParamLiteral {
33 fn into_param_literal(self) -> ParamLiteral {
34 self
35 }
36}
37
38macro_rules! impl_into_param_literal {
39 ($($rust_ty:ty => $value_variant:ident),+ $(,)?) => {
40 $(
41 impl IntoParamLiteral for $rust_ty {
42 fn into_param_literal(self) -> ParamLiteral {
43 ParamLiteral(Value::$value_variant(self))
44 }
45 }
46 )+
47 };
48}
49
50impl_into_param_literal!(
51 bool => Bool,
52 i8 => I8,
53 i16 => I16,
54 i32 => I32,
55 i64 => I64,
56 i128 => I128,
57 u8 => U8,
58 u16 => U16,
59 u32 => U32,
60 u64 => U64,
61 u128 => U128,
62 f32 => F32,
63 f64 => F64,
64 Decimal => Decimal,
65 String => Str,
66 Vec<u8> => Bytea,
67 IpAddr => Inet,
68 NaiveDate => Date,
69 NaiveTime => Time,
70 NaiveDateTime => Timestamp,
71 Point => Point,
72 Interval => Interval,
73);
74
75impl IntoParamLiteral for isize {
77 fn into_param_literal(self) -> ParamLiteral {
78 ParamLiteral(Value::I64(self as i64))
79 }
80}
81
82impl IntoParamLiteral for usize {
83 fn into_param_literal(self) -> ParamLiteral {
84 ParamLiteral(Value::U64(self as u64))
85 }
86}
87
88impl IntoParamLiteral for &str {
89 fn into_param_literal(self) -> ParamLiteral {
90 ParamLiteral(Value::Str(self.to_owned()))
91 }
92}
93
94impl IntoParamLiteral for &[u8] {
95 fn into_param_literal(self) -> ParamLiteral {
96 ParamLiteral(Value::Bytea(self.to_vec()))
97 }
98}
99
100impl IntoParamLiteral for Uuid {
101 fn into_param_literal(self) -> ParamLiteral {
102 ParamLiteral(Value::Uuid(self.as_u128()))
103 }
104}
105
106impl<T> IntoParamLiteral for Option<T>
107where
108 T: IntoParamLiteral,
109{
110 fn into_param_literal(self) -> ParamLiteral {
111 match self {
112 Some(value) => value.into_param_literal(),
113 None => ParamLiteral::null(),
114 }
115 }
116}
117
118#[macro_export]
119macro_rules! params {
120 ($($expr:expr),* $(,)?) => {
121 vec![
122 $(
123 $crate::translate::IntoParamLiteral::into_param_literal($expr)
124 ),*
125 ]
126 };
127}
128
129#[cfg(test)]
130mod tests {
131 use {
132 super::*,
133 crate::{ast::Expr, data::Point},
134 chrono::{NaiveDate, NaiveTime},
135 rust_decimal::Decimal,
136 std::{net::IpAddr, str::FromStr},
137 uuid::Uuid,
138 };
139
140 #[test]
141 fn accepts_param_literal() {
142 let literal = ParamLiteral::null();
143 let converted = literal.clone().into_param_literal();
144 assert!(matches!(literal.into_expr(), Expr::Value(Value::Null)));
145 assert!(matches!(converted.into_expr(), Expr::Value(Value::Null)));
146 }
147
148 #[test]
149 fn converts_basic_literals() {
150 let expr = true.into_param_literal().into_expr();
151 assert_eq!(expr, Expr::Value(Value::Bool(true)));
152
153 let expr = 42_i64.into_param_literal().into_expr();
154 assert_eq!(expr, Expr::Value(Value::I64(42)));
155
156 let expr = "glue".into_param_literal().into_expr();
157 assert_eq!(expr, Expr::Value(Value::Str("glue".to_owned())));
158
159 let expr = String::from("owned").into_param_literal().into_expr();
160 assert_eq!(expr, Expr::Value(Value::Str("owned".to_owned())));
161
162 let expr = 3_i16.into_param_literal().into_expr();
163 assert_eq!(expr, Expr::Value(Value::I16(3)));
164
165 let expr = 7_u32.into_param_literal().into_expr();
166 assert_eq!(expr, Expr::Value(Value::U32(7)));
167 }
168
169 #[test]
170 fn converts_typed_literals() {
171 let date = NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
172 let expr = date.into_param_literal().into_expr();
173 assert_eq!(expr, Expr::Value(Value::Date(date)));
174
175 let time = NaiveTime::from_hms_opt(9, 45, 30).unwrap();
176 let expr = time.into_param_literal().into_expr();
177 assert_eq!(expr, Expr::Value(Value::Time(time)));
178
179 let timestamp = NaiveDate::from_ymd_opt(2024, 1, 15)
180 .unwrap()
181 .and_hms_opt(12, 0, 0)
182 .unwrap();
183 let expr = timestamp.into_param_literal().into_expr();
184 assert_eq!(expr, Expr::Value(Value::Timestamp(timestamp)));
185
186 let timestamp_with_fraction = NaiveDate::from_ymd_opt(2024, 1, 15)
187 .unwrap()
188 .and_hms_micro_opt(12, 0, 0, 123_456)
189 .unwrap();
190 let expr = timestamp_with_fraction.into_param_literal().into_expr();
191 assert_eq!(expr, Expr::Value(Value::Timestamp(timestamp_with_fraction)));
192 }
193
194 #[test]
195 fn converts_interval() {
196 let expr = Interval::Month(2).into_param_literal().into_expr();
197 assert_eq!(expr, Expr::Value(Value::Interval(Interval::Month(2))));
198
199 let expr = Interval::Microsecond(1_500_000)
200 .into_param_literal()
201 .into_expr();
202 assert_eq!(
203 expr,
204 Expr::Value(Value::Interval(Interval::Microsecond(1_500_000)))
205 );
206 }
207
208 #[test]
209 fn converts_option() {
210 let expr = Some(1_i32).into_param_literal().into_expr();
211 assert_eq!(expr, Expr::Value(Value::I32(1)));
212
213 let expr = (None::<i32>).into_param_literal().into_expr();
214 assert_eq!(expr, Expr::Value(Value::Null));
215 }
216
217 #[test]
218 fn converts_scalars_and_structs() {
219 let expr = 1.25_f64.into_param_literal().into_expr();
220 assert_eq!(expr, Expr::Value(Value::F64(1.25)));
221
222 let expr = 1.5_f32.into_param_literal().into_expr();
223 assert_eq!(expr, Expr::Value(Value::F32(1.5)));
224
225 let expr = f32::NAN.into_param_literal().into_expr();
227 assert!(matches!(expr, Expr::Value(Value::F32(v)) if v.is_nan()));
228
229 let expr = f64::INFINITY.into_param_literal().into_expr();
230 assert_eq!(expr, Expr::Value(Value::F64(f64::INFINITY)));
231
232 let expr = 42_isize.into_param_literal().into_expr();
233 assert_eq!(expr, Expr::Value(Value::I64(42)));
234
235 let expr = 42_usize.into_param_literal().into_expr();
236 assert_eq!(expr, Expr::Value(Value::U64(42)));
237
238 let expr = Decimal::new(345, 2).into_param_literal().into_expr();
239 assert_eq!(expr, Expr::Value(Value::Decimal(Decimal::new(345, 2))));
240
241 let expr = vec![0x12_u8, 0xAB].into_param_literal().into_expr();
242 assert_eq!(expr, Expr::Value(Value::Bytea(vec![0x12, 0xAB])));
243
244 let bytes = [0xCD_u8, 0xEF];
245 let expr = bytes.as_slice().into_param_literal().into_expr();
246 assert_eq!(expr, Expr::Value(Value::Bytea(vec![0xCD, 0xEF])));
247
248 let ip = IpAddr::from_str("127.0.0.1").unwrap();
249 let expr = ip.into_param_literal().into_expr();
250 assert_eq!(expr, Expr::Value(Value::Inet(ip)));
251
252 let uuid = Uuid::parse_str("123e4567-e89b-12d3-a456-426614174000").unwrap();
253 let expr = uuid.into_param_literal().into_expr();
254 assert_eq!(expr, Expr::Value(Value::Uuid(uuid.as_u128())));
255
256 let point = Point::new(1.0, 2.0);
257 let expr = point.into_param_literal().into_expr();
258 assert_eq!(expr, Expr::Value(Value::Point(point)));
259 }
260
261 #[test]
262 fn params_macro_collects_literals() {
263 let params = crate::params![1_i64, "Glue", Some(false), None::<i32>];
264
265 assert_eq!(params.len(), 4);
266
267 assert_eq!(params[0].clone().into_expr(), Expr::Value(Value::I64(1)));
268 assert_eq!(
269 params[1].clone().into_expr(),
270 Expr::Value(Value::Str("Glue".to_owned()))
271 );
272 assert_eq!(
273 params[2].clone().into_expr(),
274 Expr::Value(Value::Bool(false))
275 );
276 assert_eq!(params[3].clone().into_expr(), Expr::Value(Value::Null));
277 }
278}