1use crate::error::MigrationError;
4use crate::state::{ConstraintState, FieldState, IndexState, ModelState, ProjectState};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14#[serde(tag = "op", rename_all = "snake_case")]
15pub enum Operation {
16 CreateModel {
18 model: ModelState,
20 },
21 DeleteModel {
23 name: String,
25 },
26 RenameModel {
29 old_name: String,
31 new_name: String,
33 table: String,
35 },
36 AddField {
38 model: String,
40 field: FieldState,
42 },
43 RemoveField {
45 model: String,
47 name: String,
49 },
50 AlterField {
52 model: String,
54 name: String,
56 field: FieldState,
58 },
59 RenameField {
63 model: String,
65 old_name: String,
67 new_name: String,
70 },
71 CreateIndex {
73 model: String,
75 index: IndexState,
77 },
78 DeleteIndex {
80 model: String,
82 name: String,
84 },
85 AddConstraint {
87 model: String,
89 constraint: ConstraintState,
91 },
92 DeleteConstraint {
94 model: String,
96 name: String,
98 },
99 RunSQL {
101 sql: String,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
105 reverse_sql: Option<String>,
106 },
107 RunRust {
111 name: String,
113 #[serde(default, skip_serializing_if = "Option::is_none")]
115 backwards: Option<String>,
116 },
117}
118
119impl Operation {
120 pub fn apply_to_state(&self, state: &mut ProjectState) -> Result<(), MigrationError> {
126 match self {
127 Self::CreateModel { model } => {
128 if state.models.contains_key(&model.name) {
129 return Err(MigrationError::state(format!(
130 "model `{}` already exists",
131 model.name
132 )));
133 }
134 state.models.insert(model.name.clone(), model.clone());
135 }
136 Self::DeleteModel { name } => {
137 state.models.remove(name).ok_or_else(|| {
138 MigrationError::state(format!("model `{name}` is not in the project state"))
139 })?;
140 }
141 Self::RenameModel {
142 old_name,
143 new_name,
144 table,
145 } => {
146 let mut model = state.models.remove(old_name).ok_or_else(|| {
147 MigrationError::state(format!("model `{old_name}` is not in the project state"))
148 })?;
149 if old_name != new_name && state.models.contains_key(new_name) {
150 return Err(MigrationError::state(format!(
151 "model `{new_name}` already exists"
152 )));
153 }
154 let old_table = model.table.clone();
155 model.name = new_name.clone();
156 model.table = table.clone();
157 state.models.insert(new_name.clone(), model);
158 if old_table != *table {
163 for other in state.models.values_mut() {
164 for field in &mut other.fields {
165 if let Some(fk) = field.fk.as_mut()
166 && fk.target_table == old_table
167 {
168 fk.target_table = table.clone();
169 }
170 }
171 }
172 }
173 }
174 Self::AddField { model, field } => {
175 let m = state.require_mut(model)?;
176 if m.field(&field.name).is_some() {
177 return Err(MigrationError::state(format!(
178 "field `{}` already exists on `{model}`",
179 field.name
180 )));
181 }
182 m.fields.push(field.clone());
183 }
184 Self::RemoveField { model, name } => {
185 let m = state.require_mut(model)?;
186 let before = m.fields.len();
187 m.fields.retain(|f| f.name != *name);
188 if m.fields.len() == before {
189 return Err(MigrationError::state(format!(
190 "field `{name}` not on model `{model}`"
191 )));
192 }
193 }
194 Self::AlterField { model, name, field } => {
195 let m = state.require_mut(model)?;
196 let existing = m.field_mut(name).ok_or_else(|| {
197 MigrationError::state(format!("field `{name}` not on model `{model}`"))
198 })?;
199 *existing = field.clone();
200 }
201 Self::RenameField {
202 model,
203 old_name,
204 new_name,
205 } => rename_field(state.require_mut(model)?, old_name, new_name)?,
206 Self::CreateIndex { model, index } => {
207 let m = state.require_mut(model)?;
208 if m.index(&index.name).is_some() {
209 return Err(MigrationError::state(format!(
210 "index `{}` already exists on `{model}`",
211 index.name
212 )));
213 }
214 m.indexes.push(index.clone());
215 }
216 Self::DeleteIndex { model, name } => {
217 let m = state.require_mut(model)?;
218 let before = m.indexes.len();
219 m.indexes.retain(|i| i.name != *name);
220 if m.indexes.len() == before {
221 return Err(MigrationError::state(format!(
222 "index `{name}` not on model `{model}`"
223 )));
224 }
225 }
226 Self::AddConstraint { model, constraint } => {
227 let m = state.require_mut(model)?;
228 if m.constraint(constraint.name()).is_some() {
229 return Err(MigrationError::state(format!(
230 "constraint `{}` already exists on `{model}`",
231 constraint.name()
232 )));
233 }
234 m.constraints.push(constraint.clone());
235 }
236 Self::DeleteConstraint { model, name } => {
237 let m = state.require_mut(model)?;
238 let before = m.constraints.len();
239 m.constraints.retain(|c| c.name() != *name);
240 if m.constraints.len() == before {
241 return Err(MigrationError::state(format!(
242 "constraint `{name}` not on model `{model}`"
243 )));
244 }
245 }
246 Self::RunSQL { .. } | Self::RunRust { .. } => {}
247 }
248 Ok(())
249 }
250
251 pub fn reverse(&self, state_before: &ProjectState) -> Option<Operation> {
257 match self {
258 Self::CreateModel { model } => Some(Self::DeleteModel {
259 name: model.name.clone(),
260 }),
261 Self::DeleteModel { name } => {
262 let model = state_before.model(name)?.clone();
263 Some(Self::CreateModel { model })
264 }
265 Self::RenameModel {
266 old_name, new_name, ..
267 } => {
268 let table = state_before.model(old_name)?.table.clone();
269 Some(Self::RenameModel {
270 old_name: new_name.clone(),
271 new_name: old_name.clone(),
272 table,
273 })
274 }
275 Self::AddField { model, field } => Some(Self::RemoveField {
276 model: model.clone(),
277 name: field.name.clone(),
278 }),
279 Self::RemoveField { model, name } => {
280 let field = state_before.model(model)?.field(name)?.clone();
281 Some(Self::AddField {
282 model: model.clone(),
283 field,
284 })
285 }
286 Self::AlterField { model, name, .. } => {
287 let field = state_before.model(model)?.field(name)?.clone();
288 Some(Self::AlterField {
289 model: model.clone(),
290 name: field.name.clone(),
291 field,
292 })
293 }
294 Self::RenameField {
295 model,
296 old_name,
297 new_name,
298 } => Some(Self::RenameField {
299 model: model.clone(),
300 old_name: new_name.clone(),
301 new_name: old_name.clone(),
302 }),
303 Self::CreateIndex { model, index } => Some(Self::DeleteIndex {
304 model: model.clone(),
305 name: index.name.clone(),
306 }),
307 Self::DeleteIndex { model, name } => {
308 let index = state_before.model(model)?.index(name)?.clone();
309 Some(Self::CreateIndex {
310 model: model.clone(),
311 index,
312 })
313 }
314 Self::AddConstraint { model, constraint } => Some(Self::DeleteConstraint {
315 model: model.clone(),
316 name: constraint.name().to_owned(),
317 }),
318 Self::DeleteConstraint { model, name } => {
319 let constraint = state_before.model(model)?.constraint(name)?.clone();
320 Some(Self::AddConstraint {
321 model: model.clone(),
322 constraint,
323 })
324 }
325 Self::RunSQL { reverse_sql, .. } => reverse_sql.as_ref().map(|sql| Self::RunSQL {
326 sql: sql.clone(),
327 reverse_sql: None,
328 }),
329 Self::RunRust { name, backwards } => backwards.as_ref().map(|back| Self::RunRust {
330 name: back.clone(),
331 backwards: Some(name.clone()),
332 }),
333 }
334 }
335
336 pub fn is_unconditionally_irreversible(&self) -> bool {
339 match self {
340 Self::RunSQL { reverse_sql, .. } => reverse_sql.is_none(),
341 Self::RunRust { backwards, .. } => backwards.is_none(),
342 _ => false,
343 }
344 }
345
346 pub fn summary(&self) -> String {
348 match self {
349 Self::CreateModel { model } => format!("CreateModel {}", model.name),
350 Self::DeleteModel { name } => format!("DeleteModel {name}"),
351 Self::RenameModel {
352 old_name, new_name, ..
353 } => format!("RenameModel {old_name} -> {new_name}"),
354 Self::AddField { model, field } => format!("AddField {model}.{}", field.name),
355 Self::RemoveField { model, name } => format!("RemoveField {model}.{name}"),
356 Self::AlterField { model, name, .. } => format!("AlterField {model}.{name}"),
357 Self::RenameField {
358 model,
359 old_name,
360 new_name,
361 } => format!("RenameField {model}.{old_name} -> {new_name}"),
362 Self::CreateIndex { model, index } => format!("CreateIndex {model}.{}", index.name),
363 Self::DeleteIndex { model, name } => format!("DeleteIndex {model}.{name}"),
364 Self::AddConstraint { model, constraint } => {
365 format!("AddConstraint {model}.{}", constraint.name())
366 }
367 Self::DeleteConstraint { model, name } => format!("DeleteConstraint {model}.{name}"),
368 Self::RunSQL { .. } => "RunSQL".to_owned(),
369 Self::RunRust { name, .. } => format!("RunRust {name}"),
370 }
371 }
372}
373
374fn rename_field(
375 model: &mut ModelState,
376 old_name: &str,
377 new_name: &str,
378) -> Result<(), MigrationError> {
379 if model.field(new_name).is_some() && old_name != new_name {
380 return Err(MigrationError::state(format!(
381 "field `{new_name}` already exists on `{}`",
382 model.name
383 )));
384 }
385 let rename_column = {
386 let field = model.field(old_name).ok_or_else(|| {
387 MigrationError::state(format!("field `{old_name}` not on model `{}`", model.name))
388 })?;
389 field.column == old_name
390 };
391 let old_column = model
392 .field(old_name)
393 .map(|f| f.column.clone())
394 .unwrap_or_default();
395 let model_name = model.name.clone();
396 let field = model.field_mut(old_name).ok_or_else(|| {
397 MigrationError::state(format!("field `{old_name}` not on model `{model_name}`"))
398 })?;
399 field.name = new_name.to_owned();
400 if rename_column {
401 field.column = new_name.to_owned();
402 }
403 if rename_column {
404 let new_column = new_name.to_owned();
405 for index in &mut model.indexes {
406 for col in &mut index.columns {
407 if *col == old_column {
408 *col = new_column.clone();
409 }
410 }
411 }
412 for constraint in &mut model.constraints {
413 if let crate::state::ConstraintState::Unique { columns, .. } = constraint {
414 for col in columns {
415 if *col == old_column {
416 *col = new_column.clone();
417 }
418 }
419 }
420 }
421 }
422 Ok(())
423}