typed_sql/query/
insert.rs

1use super::Select;
2use crate::{CheckedSql, Table, ToSql};
3use std::marker::PhantomData;
4
5pub trait Insertable {
6    fn write_columns(sql: &mut String);
7
8    fn write_values(&self, sql: &mut String);
9}
10
11impl<I: Insertable> Insertable for &I {
12    fn write_columns(sql: &mut String) {
13        I::write_columns(sql);
14    }
15
16    fn write_values(&self, sql: &mut String) {
17        (*self).write_values(sql);
18    }
19}
20
21pub struct Values<I> {
22    iter: I,
23}
24
25impl<I> Values<I> {
26    pub(crate) fn new(iter: I) -> Self {
27        Self { iter }
28    }
29}
30
31pub struct InsertSelect<S, I: ?Sized> {
32    select: S,
33    _insertable: PhantomData<I>,
34}
35
36impl<S, I: ?Sized> InsertSelect<S, I> {
37    pub(crate) fn new(select: S) -> Self {
38        Self {
39            select,
40            _insertable: PhantomData,
41        }
42    }
43}
44
45impl<S, I> Insertable for InsertSelect<S, I>
46where
47    S: Select,
48    I: Insertable + ?Sized,
49{
50    fn write_columns(sql: &mut String) {
51        I::write_columns(sql);
52    }
53
54    fn write_values(&self, sql: &mut String) {
55        self.select.write_sql_unchecked(sql);
56    }
57}
58
59pub struct InsertStatement<T: ?Sized, V> {
60    values: V,
61    _table: PhantomData<T>,
62}
63
64impl<T, I> InsertStatement<T, I>
65where
66    T: Table + ?Sized,
67{
68    pub(crate) fn new(values: I) -> Self {
69        Self {
70            values,
71            _table: PhantomData,
72        }
73    }
74
75    fn write_insert<I2: Insertable>(&self, sql: &mut String) {
76        sql.push_str("INSERT INTO ");
77        sql.push_str(T::NAME);
78        sql.push('(');
79        I2::write_columns(sql);
80        sql.push(')');
81        sql.push_str(" VALUES ");
82    }
83}
84
85impl<T, I> ToSql for InsertStatement<T, I>
86where
87    T: Table + ?Sized,
88    I: Insertable,
89{
90    fn write_sql_unchecked(&self, sql: &mut String) {
91        self.write_insert::<I>(sql);
92
93        sql.push('(');
94        self.values.write_values(sql);
95        sql.push(')');
96    }
97}
98
99impl<T, I> ToSql for InsertStatement<T, Values<I>>
100where
101    T: Table,
102    I: IntoIterator + Clone,
103    I::Item: Insertable,
104{
105    fn write_sql_unchecked(&self, sql: &mut String) {
106        self.write_insert::<I::Item>(sql);
107
108        let mut values = self.values.iter.clone().into_iter().peekable();
109        loop {
110            if let Some(value) = values.next() {
111                sql.push('(');
112                value.write_values(sql);
113                sql.push(')');
114
115                if values.peek().is_some() {
116                    sql.push(',');
117                    continue;
118                }
119            }
120            break;
121        }
122    }
123}
124
125impl<T, I: CheckedSql> CheckedSql for InsertSelect<T, I> {}