chopin_orm/
active_model.rs1use crate::{Executor, Model, OrmError, OrmResult, PgValue};
2
3#[derive(Clone, Debug, PartialEq)]
5pub enum ActiveValue<V> {
6 Set(V),
8 Unchanged(V),
10 NotSet,
12}
13
14impl<V> ActiveValue<V> {
15 pub fn is_set(&self) -> bool {
17 matches!(self, Self::Set(_))
18 }
19
20 pub fn into_value(self) -> Option<V> {
22 match self {
23 Self::Set(v) | Self::Unchanged(v) => Some(v),
24 Self::NotSet => None,
25 }
26 }
27}
28
29pub struct ActiveModel<M: Model> {
34 pub inner: M,
36 changes: Vec<(&'static str, ActiveValue<PgValue>)>,
38 is_new: bool,
40}
41
42impl<M: Model> ActiveModel<M> {
43 pub fn new_insert(model: M) -> Self {
45 Self {
46 inner: model,
47 changes: Vec::new(),
48 is_new: true,
49 }
50 }
51
52 pub fn from_model(model: M) -> Self {
54 Self {
55 inner: model,
56 changes: Vec::new(),
57 is_new: false,
58 }
59 }
60
61 pub fn set<T: crate::ToSql>(&mut self, column: &'static str, value: T) {
63 let sql_val = value.to_sql();
64 if let Some(existing) = self.changes.iter_mut().find(|(c, _)| *c == column) {
65 existing.1 = ActiveValue::Set(sql_val);
66 } else {
67 self.changes.push((column, ActiveValue::Set(sql_val)));
68 }
69 }
70
71 pub fn has_changes(&self) -> bool {
73 self.changes.iter().any(|(_, v)| v.is_set())
74 }
75
76 pub fn changed_columns(&self) -> Vec<&'static str> {
78 self.changes
79 .iter()
80 .filter(|(_, v)| v.is_set())
81 .map(|(c, _)| *c)
82 .collect()
83 }
84
85 pub fn is_new(&self) -> bool {
87 self.is_new
88 }
89
90 fn validate(&self) -> OrmResult<()> {
92 if let Err(errors) = self.inner.validate() {
93 return Err(OrmError::Validation(errors));
94 }
95 Ok(())
96 }
97
98 pub fn save(&mut self, executor: &mut impl Executor) -> OrmResult<()> {
102 self.validate()?;
103 if self.is_new() {
104 self.insert(executor)?;
105 self.is_new = false;
106 Ok(())
107 } else {
108 self.update(executor)
109 }
110 }
111
112 pub fn insert(&mut self, executor: &mut impl Executor) -> OrmResult<()> {
116 self.validate()?;
117
118 if !self.has_changes() {
120 return self.inner.insert(executor);
121 }
122
123 let mut cols = Vec::new();
124 let mut vals = Vec::new();
125 for (c, v) in &self.changes {
126 if let ActiveValue::Set(val) = v {
127 cols.push(*c);
128 vals.push(val.clone());
129 }
130 }
131
132 let bindings: Vec<String> = (1..=cols.len()).map(|i| format!("${}", i)).collect();
133 let query = format!(
134 "INSERT INTO {} ({}) VALUES ({}) RETURNING {}",
135 M::table_name(),
136 cols.join(", "),
137 bindings.join(", "),
138 M::columns().join(", ")
139 );
140
141 let params: Vec<&dyn chopin_pg::types::ToSql> = vals.iter().map(|v| v as _).collect();
142 let rows = executor.query(&query, ¶ms)?;
143
144 if let Some(row) = rows.first() {
145 self.inner = M::from_row(row)?;
146 self.changes.clear();
147 Ok(())
148 } else {
149 Err(OrmError::ModelError(
150 "Insert failed, no rows returned".to_string(),
151 ))
152 }
153 }
154
155 pub fn update(&mut self, executor: &mut impl Executor) -> OrmResult<()> {
159 self.validate()?;
160 if !self.has_changes() {
161 return Ok(());
162 }
163
164 let mut set_clauses = Vec::new();
165 let mut query_values = Vec::new();
166 let mut param_idx = 1;
167
168 for (col, val) in &self.changes {
169 if let ActiveValue::Set(v) = val {
170 set_clauses.push(format!("{} = ${}", col, param_idx));
171 query_values.push(v.clone());
172 param_idx += 1;
173 }
174 }
175
176 let mut where_clauses = Vec::new();
177 let pk_cols = M::primary_key_columns();
178 let pk_vals = self.inner.primary_key_values();
179
180 for (i, col) in pk_cols.iter().enumerate() {
181 where_clauses.push(format!("{} = ${}", col, param_idx));
182 query_values.push(pk_vals[i].clone());
183 param_idx += 1;
184 }
185
186 let query = format!(
187 "UPDATE {} SET {} WHERE {} RETURNING {}",
188 M::table_name(),
189 set_clauses.join(", "),
190 where_clauses.join(" AND "),
191 M::columns().join(", ")
192 );
193
194 let params: Vec<&dyn chopin_pg::types::ToSql> =
195 query_values.iter().map(|v| v as _).collect();
196 let rows = executor.query(&query, ¶ms)?;
197
198 if let Some(row) = rows.first() {
199 self.inner = M::from_row(row)?;
200 self.changes.clear();
201 Ok(())
202 } else {
203 Err(OrmError::ModelError(
204 "Update failed, no rows returned".to_string(),
205 ))
206 }
207 }
208}
209
210impl<M: Model> From<M> for ActiveModel<M> {
211 fn from(model: M) -> Self {
212 ActiveModel::from_model(model)
213 }
214}