lsor_core/
driver.rs

1use std::fmt::Display;
2
3use chrono::{DateTime, Utc};
4use sqlx::{postgres::PgArguments, Database, Encode, Executor, Postgres, Type};
5use uuid::Uuid;
6
7pub struct Driver {
8    prql: String,
9    arguments: PgArguments,
10}
11
12impl Driver {
13    pub fn new() -> Self {
14        Driver {
15            prql: String::new(),
16            arguments: PgArguments::default(),
17        }
18    }
19
20    pub fn prql(&self) -> &str {
21        &self.prql
22    }
23
24    pub fn sql(&self) -> String {
25        use prqlc::{sql::Dialect, Options, Target};
26
27        let opts = &Options {
28            format: false,
29            signature_comment: false,
30            target: Target::Sql(Some(Dialect::Postgres)),
31            ..Default::default()
32        };
33
34        match prqlc::compile(&self.prql, opts) {
35            Ok(sql) => {
36                tracing::debug!("compiling prql:\n{}\ninto sql:\n{}", &self.prql, &sql);
37                sql
38            }
39            Err(e) => {
40                tracing::error!("bad prql:\n{}", &self.prql);
41                panic!("must compile prql: {}", e)
42            }
43        }
44    }
45
46    pub fn is_empty(&self) -> bool {
47        self.prql.is_empty()
48    }
49
50    pub fn push(&mut self, prql: impl Display) {
51        use std::fmt::Write as _;
52
53        write!(&mut self.prql, "{}", prql).expect("must write pqrl");
54    }
55
56    pub fn push_bind<T>(&mut self, value: T)
57    where
58        for<'q> T: Encode<'q, Postgres> + Send + Type<Postgres>,
59    {
60        use sqlx::Arguments as _;
61
62        self.arguments.add(value);
63        self.arguments
64            .format_placeholder(&mut self.prql)
65            .expect("must format placeholder");
66    }
67
68    pub async fn execute_without_compilation<'c>(
69        self,
70        executor: impl Executor<'c, Database = Postgres>,
71    ) -> sqlx::Result<<Postgres as Database>::QueryResult> {
72        use sqlx::QueryBuilder;
73
74        QueryBuilder::with_arguments(self.prql, self.arguments)
75            .build()
76            .execute(executor)
77            .await
78    }
79
80    pub async fn fetch_all(
81        self,
82        executor: impl Executor<'_, Database = Postgres>,
83    ) -> sqlx::Result<Vec<<Postgres as Database>::Row>> {
84        use sqlx::QueryBuilder;
85
86        QueryBuilder::with_arguments(self.sql(), self.arguments)
87            .build()
88            .fetch_all(executor)
89            .await
90    }
91
92    pub async fn fetch_one(
93        self,
94        executor: impl Executor<'_, Database = Postgres>,
95    ) -> sqlx::Result<<Postgres as Database>::Row> {
96        use sqlx::QueryBuilder;
97
98        QueryBuilder::with_arguments(self.sql(), self.arguments)
99            .build()
100            .fetch_one(executor)
101            .await
102    }
103
104    pub async fn fetch_optional(
105        self,
106        executor: impl Executor<'_, Database = Postgres>,
107    ) -> sqlx::Result<Option<<Postgres as Database>::Row>> {
108        use sqlx::QueryBuilder;
109
110        QueryBuilder::with_arguments(self.sql(), self.arguments)
111            .build()
112            .fetch_optional(executor)
113            .await
114    }
115}
116
117impl Default for Driver {
118    fn default() -> Self {
119        Self::new()
120    }
121}
122
123pub trait PushPrql {
124    fn push_to_driver(&self, driver: &mut Driver);
125}
126
127impl PushPrql for String {
128    fn push_to_driver(&self, driver: &mut Driver) {
129        driver.push_bind(self);
130    }
131}
132
133impl PushPrql for &str {
134    fn push_to_driver(&self, driver: &mut Driver) {
135        driver.push_bind(self);
136    }
137}
138
139impl PushPrql for i32 {
140    fn push_to_driver(&self, driver: &mut Driver) {
141        driver.push_bind(self);
142    }
143}
144
145impl PushPrql for i64 {
146    fn push_to_driver(&self, driver: &mut Driver) {
147        driver.push_bind(self);
148    }
149}
150
151impl PushPrql for u32 {
152    fn push_to_driver(&self, driver: &mut Driver) {
153        driver.push_bind(*self as i32);
154    }
155}
156
157impl PushPrql for u64 {
158    fn push_to_driver(&self, driver: &mut Driver) {
159        driver.push_bind(*self as i64);
160    }
161}
162
163impl PushPrql for bool {
164    fn push_to_driver(&self, driver: &mut Driver) {
165        driver.push_bind(self);
166    }
167}
168
169impl PushPrql for Uuid {
170    fn push_to_driver(&self, driver: &mut Driver) {
171        driver.push_bind(self);
172    }
173}
174
175impl PushPrql for DateTime<Utc> {
176    fn push_to_driver(&self, driver: &mut Driver) {
177        driver.push_bind(self);
178    }
179}
180
181impl<T> PushPrql for Option<T>
182where
183    for<'q> T: 'q + Encode<'q, Postgres> + Sync + Type<Postgres>,
184{
185    fn push_to_driver(&self, driver: &mut Driver) {
186        driver.push_bind(self);
187    }
188}
189
190impl<T> PushPrql for Vec<T>
191where
192    T: PushPrql,
193{
194    fn push_to_driver(&self, driver: &mut Driver) {
195        for (i, value) in self.iter().enumerate() {
196            if i > 0 {
197                driver.push(", ");
198            }
199            value.push_to_driver(driver);
200        }
201    }
202}
203
204impl<T> PushPrql for &T
205where
206    T: PushPrql,
207{
208    fn push_to_driver(&self, driver: &mut Driver) {
209        (*self).push_to_driver(driver);
210    }
211}
212
213impl PushPrql for &dyn PushPrql {
214    fn push_to_driver(&self, driver: &mut Driver) {
215        (*self).push_to_driver(driver)
216    }
217}
218
219pub fn sql(sql: &'static str) -> SQL {
220    SQL { sql }
221}
222
223pub struct SQL {
224    pub sql: &'static str,
225}
226
227impl PushPrql for SQL {
228    fn push_to_driver(&self, driver: &mut crate::driver::Driver) {
229        driver.push("s\"");
230        driver.push(self.sql);
231        driver.push('\"');
232    }
233}