1use std::fmt::Write;
2
3use crate::conditional::{BuildCondition, Condition};
4#[cfg(feature = "postgres")]
5use crate::db_specific::postgres;
6#[cfg(feature = "sqlite")]
7use crate::db_specific::sqlite;
8use crate::error::Error;
9use crate::value::NullType;
10use crate::{DBImpl, OnConflict, Value};
11
12pub trait Update<'until_build, 'post_build> {
16 fn rollback_transaction(self) -> Self;
26
27 fn where_clause(self, condition: &'until_build Condition<'post_build>) -> Self;
31
32 fn add_update(self, column_name: &'until_build str, column_value: Value<'post_build>) -> Self;
40
41 fn build(self) -> Result<(String, Vec<Value<'post_build>>), Error>;
49}
50
51#[derive(Debug)]
55pub struct UpdateData<'until_build, 'post_build> {
56 pub(crate) model: &'until_build str,
57 pub(crate) on_conflict: OnConflict,
58 pub(crate) updates: Vec<(&'until_build str, Value<'post_build>)>,
59 pub(crate) where_clause: Option<&'until_build Condition<'post_build>>,
60 pub(crate) lookup: Vec<Value<'post_build>>,
61}
62
63#[derive(Debug)]
69pub enum UpdateImpl<'until_build, 'post_build> {
70 #[cfg(feature = "sqlite")]
74 SQLite(UpdateData<'until_build, 'post_build>),
75 #[cfg(feature = "postgres")]
79 Postgres(UpdateData<'until_build, 'post_build>),
80}
81
82impl<'until_build, 'post_build> Update<'until_build, 'post_build>
83 for UpdateImpl<'until_build, 'post_build>
84{
85 fn rollback_transaction(mut self) -> Self {
86 match self {
87 #[cfg(feature = "sqlite")]
88 UpdateImpl::SQLite(ref mut d) => d.on_conflict = OnConflict::ROLLBACK,
89 #[cfg(feature = "postgres")]
90 UpdateImpl::Postgres(ref mut d) => d.on_conflict = OnConflict::ROLLBACK,
91 };
92 self
93 }
94
95 fn where_clause(mut self, condition: &'until_build Condition<'post_build>) -> Self {
96 match self {
97 #[cfg(feature = "sqlite")]
98 UpdateImpl::SQLite(ref mut d) => d.where_clause = Some(condition),
99 #[cfg(feature = "postgres")]
100 UpdateImpl::Postgres(ref mut d) => d.where_clause = Some(condition),
101 };
102 self
103 }
104
105 fn add_update(
106 mut self,
107 column_name: &'until_build str,
108 column_value: Value<'post_build>,
109 ) -> Self {
110 match self {
111 #[cfg(feature = "sqlite")]
112 UpdateImpl::SQLite(ref mut d) => d.updates.push((column_name, column_value)),
113 #[cfg(feature = "postgres")]
114 UpdateImpl::Postgres(ref mut d) => d.updates.push((column_name, column_value)),
115 };
116 self
117 }
118
119 fn build(self) -> Result<(String, Vec<Value<'post_build>>), Error> {
120 match self {
121 #[cfg(feature = "sqlite")]
122 UpdateImpl::SQLite(mut d) => {
123 if d.updates.is_empty() {
124 return Err(Error::SQLBuildError(String::from(
125 "There must be at least one update in an UPDATE statement",
126 )));
127 }
128 let mut s = format!(
129 "UPDATE {}{} SET ",
130 match d.on_conflict {
131 OnConflict::ABORT => "OR ABORT ",
132 OnConflict::ROLLBACK => "OR ROLLBACK ",
133 },
134 d.model,
135 );
136
137 let update_index = d.updates.len() - 1;
138 for (idx, (name, value)) in d.updates.into_iter().enumerate() {
139 if let Value::Choice(c) = value {
140 write!(s, "\"{name}\" = {}", sqlite::fmt(c)).unwrap();
141 } else if let Value::Null(NullType::Choice) = value {
142 write!(s, "\"{name}\" = NULL").unwrap();
143 } else {
144 write!(s, "\"{name}\" = ?").unwrap();
145 d.lookup.push(value);
146 }
147 if idx != update_index {
148 write!(s, ", ").unwrap();
149 }
150 }
151
152 if let Some(condition) = d.where_clause {
153 write!(
154 s,
155 " WHERE {}",
156 condition.build(DBImpl::SQLite, &mut d.lookup)
157 )
158 .unwrap();
159 }
160
161 write!(s, ";").unwrap();
162
163 Ok((s, d.lookup))
164 }
165 #[cfg(feature = "postgres")]
166 UpdateImpl::Postgres(mut d) => {
167 if d.updates.is_empty() {
168 return Err(Error::SQLBuildError(String::from(
169 "There must be at least one update in an UPDATE statement",
170 )));
171 }
172 let mut s = format!("UPDATE \"{}\" SET ", d.model);
173
174 let update_index = d.updates.len() - 1;
175 for (idx, (name, value)) in d.updates.into_iter().enumerate() {
176 if let Value::Choice(c) = value {
177 write!(s, "\"{name}\" = {}", postgres::fmt(c)).unwrap();
178 } else if let Value::Null(NullType::Choice) = value {
179 write!(s, "\"{name}\" = NULL").unwrap();
180 } else {
181 d.lookup.push(value);
182 write!(s, "\"{name}\" = ${}", d.lookup.len()).unwrap();
183 }
184 if idx != update_index {
185 write!(s, ", ").unwrap();
186 }
187 }
188
189 if let Some(condition) = d.where_clause {
190 write!(
191 s,
192 " WHERE {}",
193 condition.build(DBImpl::Postgres, &mut d.lookup)
194 )
195 .unwrap();
196 }
197
198 write!(s, ";").unwrap();
199
200 Ok((s, d.lookup))
201 }
202 }
203 }
204}