sql_query/
create_table_st.rs

1use std::ops::Not;
2use std::{borrow::BorrowMut, marker::PhantomData};
3
4use constraints::dyn_object::{DynConstraint, ImplConstraint};
5use constraints::exports::not_null;
6use constraints::{Constraints, Noop};
7use foreign_key::Fk;
8use sqlx::{Database, Type};
9use types::{ColumnType, ColumnTypeStruct};
10
11use crate::{HasQuery, Query};
12
13// #[derive(Clone)]
14pub struct CreateTableSt<'q, S, Q: Query<S>> {
15    pub(crate) header: String,
16    pub(crate) ident: (Option<String>, String),
17    pub(crate) columns: Vec<(
18        String,
19        Box<dyn ColumnType + Send + Sync>,
20        Box<dyn DynConstraint<S, Q> + 'q + Send + Sync>,
21    )>,
22    pub(crate) foreign_keys: Vec<Fk>,
23    pub(crate) verbatim: Vec<String>,
24    pub(crate) ctx: Q::Context1,
25    pub(crate) _sqlx: PhantomData<S>,
26}
27
28impl<'q, S, Q> CreateTableSt<'q, S, Q>
29where
30    S: 'static + Database,
31    Q: Query<S>,
32{
33    pub fn verbatim(&mut self, verbatim: &str) -> &mut Self {
34        self.verbatim.push(verbatim.to_string());
35        self
36    }
37    pub fn column<Ty>(
38        &mut self,
39        column: &'static str,
40        con: impl Constraints<S, Q, Ty> + 'static + Send + Sync,
41    ) -> &mut Self
42    where
43        S: Sync,
44        Ty: Type<S> + 'static + Send + Sync,
45        Q::Context1: 'q,
46        Q: 'q,
47        Ty: 'q,
48    {
49        self.columns.push((
50            column.to_string(),
51            Box::new(ColumnTypeStruct::<S, Ty>(PhantomData)),
52            {
53                let bo_mut = self.ctx.borrow_mut();
54                let bo_mut = unsafe { &mut *(bo_mut as *mut _) };
55                let part = con.constraint(bo_mut);
56
57                Box::new(ImplConstraint {
58                    closure: Some(part),
59                    _pd: PhantomData::<Ty>,
60                })
61            },
62        ));
63        self
64    }
65
66    #[deprecated = "use constraint in create_table_st2"]
67    pub fn foreign_key(&mut self, fk: Fk) -> &mut Self
68    where
69        S: SqlxQuery + Sync,
70        Q: 'q,
71        <S as SqlxQuery>::KeyType: Send,
72    {
73        if fk.not_null {
74            self.column::<S::KeyType>(fk.column, not_null());
75        } else {
76            self.column::<Option<S::KeyType>>(fk.column, Noop);
77        }
78        self.foreign_keys.push(fk);
79        self
80    }
81}
82
83pub trait SqlxQuery: Database {
84    type KeyType: Type<Self>
85        + constraints::IsNull
86        + Send
87        + Sync
88        + 'static;
89    fn default_primary_key() -> &'static str;
90}
91
92impl SqlxQuery for sqlx::Sqlite {
93    type KeyType = i64;
94    fn default_primary_key() -> &'static str {
95        "PRIMARY KEY AUTOINCREMENT"
96    }
97}
98
99impl<'q, S, Q> HasQuery<S, Q> for CreateTableSt<'q, S, Q>
100where
101    S: 'static + Database,
102    Q: Query<S>,
103{
104    fn _build(self) -> (String, Q::Output) {
105        Q::build_query(self.ctx, |ctx| {
106            let mut str = String::from(&self.header);
107
108            str.push(' ');
109
110            if let Some(schema) = self.ident.0 {
111                str.push_str(&schema);
112            }
113
114            str.push_str(&self.ident.1);
115
116            str.push_str(" (");
117
118            let mut clauses = Vec::new();
119
120            for mut col in self.columns {
121                let mut str =
122                    format!("{} {}", col.0, col.1.sqlx_info());
123
124                let mut str_2 = String::new();
125                col.2.call(ctx, &mut str_2);
126
127                if str_2.is_empty().not() {
128                    str.push(' ');
129                    str.push_str(&str_2);
130                }
131
132                clauses.push(str);
133            }
134
135            for fk in self.foreign_keys {
136                let mut str = format!(
137                    "FOREIGN KEY ({}) REFERENCES {}({})",
138                    fk.column, fk.refer_table, fk.refer_column
139                );
140
141                if fk.not_null.not() {
142                    str.push_str(" ON DELETE SET NULL");
143                }
144
145                clauses.push(str);
146            }
147
148            for verbatim in self.verbatim {
149                clauses.push(verbatim);
150            }
151
152            str.push_str(&clauses.join(", "));
153            str.push_str(");");
154
155            str
156        })
157    }
158}
159
160pub mod foreign_key {
161    #[derive(Clone)]
162    #[deprecated = "use create_table_st2"]
163    pub struct Fk {
164        pub not_null: bool,
165        pub column: &'static str,
166        pub refer_table: &'static str,
167        pub refer_column: &'static str,
168    }
169}
170
171pub mod types {
172    use std::marker::PhantomData;
173
174    use sqlx::{prelude::Type, Database};
175
176    pub trait ColumnType {
177        fn sqlx_info(&self) -> String;
178        fn clone_dyn(&self) -> Box<dyn ColumnType>;
179    }
180
181    impl Clone for Box<dyn ColumnType> {
182        fn clone(&self) -> Box<dyn ColumnType> {
183            self.clone_dyn()
184        }
185    }
186    pub struct ColumnTypeStruct<S, Ty>(
187        pub(crate) PhantomData<(S, Ty)>,
188    );
189    impl<S, Ty> ColumnType for ColumnTypeStruct<S, Ty>
190    where
191        S: Database,
192        Ty: Type<S> + 'static,
193    {
194        fn clone_dyn(&self) -> Box<dyn ColumnType> {
195            Box::new(ColumnTypeStruct::<S, Ty>(PhantomData))
196        }
197        fn sqlx_info(&self) -> String {
198            use sqlx::TypeInfo;
199            Ty::type_info().name().to_string()
200        }
201    }
202}
203
204pub mod constraints {
205    use std::ops::Not;
206
207    use crate::{Accept, Query};
208
209    use super::SqlxQuery;
210
211    pub trait Constraints<S, Q: Query<S>, Ty> {
212        fn constraint(
213            self,
214            ctx1: &mut Q::Context1,
215        ) -> impl FnOnce(&mut Q::Context2, &mut String) + Send + Sync
216        where
217            Self: Sized;
218    }
219    pub mod dyn_object {
220        use std::{marker::PhantomData, mem::take};
221
222        use crate::Query;
223
224        pub trait DynConstraint<S, Q: Query<S>> {
225            fn call(
226                &mut self,
227                ctx2: &mut Q::Context2,
228                str: &mut String,
229            );
230        }
231
232        impl<'q, S, Q: Query<S>> Clone
233            for Box<dyn DynConstraint<S, Q> + 'q>
234        {
235            fn clone(&self) -> Self {
236                todo!("Clonsing constraint is not supported yet")
237            }
238        }
239
240        pub struct ImplConstraint<F: Send + Sync, T> {
241            pub closure: Option<F>,
242            pub _pd: PhantomData<T>,
243        }
244
245        impl<S, Q, F, T> DynConstraint<S, Q> for ImplConstraint<F, T>
246        where
247            Q: Query<S>,
248            F: FnOnce(&mut Q::Context2, &mut String)
249                + Send
250                + Sync,
251        {
252            fn call(
253                &mut self,
254                ctx2: &mut <Q as Query<S>>::Context2,
255                str: &mut String,
256            ) {
257                if let Some(c) = take(&mut self.closure) {
258                    c(ctx2, str)
259                } else {
260                    panic!("is there an issue with cloning")
261                }
262            }
263        }
264    }
265
266    pub struct Noop;
267
268    impl<S, Q: Query<S>, Ty> Constraints<S, Q, Ty> for Noop {
269        fn constraint(
270            self,
271            _ctx1: &mut Q::Context1,
272        ) -> impl FnOnce(&mut Q::Context2, &mut String)
273        where
274            Self: Sized,
275        {
276            |_, _| {}
277        }
278    }
279
280    pub fn noop() -> Noop {
281        Noop
282    }
283
284    impl<S, Q: Query<S>, Ty: IsNull> Constraints<S, Q, Ty> for () {
285        fn constraint(
286            self,
287            _ctx1: &mut Q::Context1,
288        ) -> impl FnOnce(&mut Q::Context2, &mut String)
289        where
290            Self: Sized,
291        {
292            |_, str| {
293                if Ty::is_null().not() {
294                    str.push_str("NOT NULL");
295                }
296            }
297        }
298    }
299
300    pub struct NotNull;
301
302    impl<S, Q: Query<S>, Ty> Constraints<S, Q, Ty> for NotNull {
303        fn constraint(
304            self,
305            _ctx1: &mut Q::Context1,
306        ) -> impl FnOnce(&mut Q::Context2, &mut String)
307        where
308            Self: Sized,
309        {
310            |_, str| str.push_str("NOT NULL")
311        }
312    }
313
314    pub struct DefaultConstraint<T>(T);
315
316    impl<S, Q, Ty, T> Constraints<S, Q, Ty> for DefaultConstraint<T>
317    where
318        Q: Accept<T, S>,
319    {
320        fn constraint(
321            self,
322            ctx1: &mut Q::Context1,
323        ) -> impl FnOnce(&mut Q::Context2, &mut String) + Send + Sync
324        where
325            Self: Sized,
326        {
327            let save = Q::accept(self.0, ctx1);
328            |ctx, str| {
329                str.push_str(&format!("DEFAULT {}", save(ctx)))
330            }
331        }
332    }
333
334    pub struct DefaultPrimaryKey;
335
336    impl<S: SqlxQuery, Q: Query<S>> Constraints<S, Q, S::KeyType>
337        for DefaultPrimaryKey
338    {
339        fn constraint(
340            self,
341            _ctx1: &mut Q::Context1,
342        ) -> impl FnOnce(&mut Q::Context2, &mut String)
343        where
344            Self: Sized,
345        {
346            |_, str| str.push_str(&S::default_primary_key())
347        }
348    }
349
350    pub struct CheckIfNull;
351
352    /// this trait should have only two impls, one default
353    /// for every type T (is_null=false), and specialized one for
354    /// Option<T> (is_null=true).
355    /// when feature specialization gets stebilized I will
356    /// simplify this trait's impls, no breaking changes will
357    /// occur.
358    pub trait IsNull {
359        fn is_null() -> bool;
360    }
361
362    impl<S, Q: Query<S>, T: IsNull> Constraints<S, Q, T>
363        for CheckIfNull
364    {
365        fn constraint(
366            self,
367            _ctx1: &mut Q::Context1,
368        ) -> impl FnOnce(&mut Q::Context2, &mut String)
369        where
370            Self: Sized,
371        {
372            |_, str| {
373                if T::is_null().not() {
374                    str.push_str("NOT NULL");
375                }
376            }
377        }
378    }
379
380    mod impl_waiting_specialization {
381        use super::IsNull;
382
383        impl<T> IsNull for Option<T> {
384            fn is_null() -> bool {
385                true
386            }
387        }
388
389        macro_rules! impl_no_gens {
390            ($($ident:ident)*) => {
391                $(impl IsNull for $ident {
392                    fn is_null() -> bool {
393                        false
394                    }
395                })*
396            };
397        }
398
399        impl_no_gens!(i32 i64 bool char String);
400    }
401
402    pub trait IsConstraint {}
403    pub trait NotNullOnlyOnce {}
404    pub trait DefaultOnlyOnce {}
405    pub trait CheckIfNullOnlyOnce {}
406
407    mod waiting_auto_feature_and_negative_impl_feature {
408        use super::*;
409
410        impl IsConstraint for () {}
411        impl IsConstraint for NotNull {}
412        impl<T> IsConstraint for DefaultConstraint<T> {}
413
414        impl NotNullOnlyOnce for () {}
415        impl DefaultOnlyOnce for () {}
416        impl CheckIfNullOnlyOnce for () {}
417
418        impl<T> NotNullOnlyOnce for DefaultConstraint<T> {}
419        impl<T> CheckIfNullOnlyOnce for DefaultConstraint<T> {}
420
421        impl DefaultOnlyOnce for NotNull {}
422        impl CheckIfNullOnlyOnce for NotNull {}
423
424        impl NotNullOnlyOnce for CheckIfNull {}
425        impl DefaultOnlyOnce for CheckIfNull {}
426
427        impl<A1, A2> NotNullOnlyOnce for And<A1, A2> where
428            A1: NotNullOnlyOnce // A2: NotNullOnlyOnce,
429        {
430        }
431    }
432
433    pub mod exports {
434        use super::CheckIfNull;
435
436        pub trait ConstraintExtention: Sized {
437            fn not_null(self) -> And<Self, NotNull>
438            where
439                Self: NotNullOnlyOnce,
440            {
441                And(self, not_null())
442            }
443            fn default<T>(
444                self,
445                t: T,
446            ) -> And<Self, DefaultConstraint<T>>
447            where
448                Self: DefaultOnlyOnce,
449            {
450                And(self, default(t))
451            }
452            fn check_if_null(self) -> And<Self, CheckIfNull>
453            where
454                Self: CheckIfNullOnlyOnce,
455            {
456                And(self, check_if_null())
457            }
458        }
459
460        impl<T: IsConstraint> ConstraintExtention for T {}
461
462        pub fn check_if_null() -> CheckIfNull {
463            CheckIfNull
464        }
465
466        use super::*;
467        pub fn not_null() -> NotNull {
468            super::NotNull
469        }
470        pub fn default<T>(t: T) -> DefaultConstraint<T> {
471            super::DefaultConstraint(t)
472        }
473        pub fn primary_key() -> DefaultPrimaryKey {
474            DefaultPrimaryKey
475        }
476    }
477
478    pub struct And<T1, T2>(T1, T2);
479
480    impl<S, Q: Query<S>, Ty, T1, T2> Constraints<S, Q, Ty>
481        for And<T1, T2>
482    where
483        T1: Constraints<S, Q, Ty>,
484        T2: Constraints<S, Q, Ty>,
485    {
486        fn constraint(
487            self,
488            ctx1: &mut Q::Context1,
489        ) -> impl FnOnce(&mut Q::Context2, &mut String)
490        where
491            Self: Sized,
492        {
493            let ctx_u1 = unsafe { &mut *(ctx1 as *mut _) };
494            let s1 = self.0.constraint(ctx_u1);
495            // let _ctx_u1 = unsafe { &mut *(ctx1 as *mut _) };
496            let s2 = self.1.constraint(ctx1);
497            |ctx, str| {
498                s1(ctx, str);
499                let mut str_inner = String::new();
500                s2(ctx, &mut str_inner);
501
502                if str_inner.is_empty().not() {
503                    str.push(' ');
504                    str.push_str(&str_inner);
505                }
506            }
507        }
508    }
509}
510
511pub mod exports {
512    pub use super::foreign_key::Fk;
513}