@@ -526,40 +526,46 @@ fn rewrite_plan_for_recreation(
526526 }
527527}
528528
529+ /// Whether the caller should continue processing the plan or return early.
529530#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
530- enum RecreateHandling {
531- NotNeeded ,
532- Rewritten ,
533- PlanEmptied ,
531+ enum RecreateOutcome {
532+ /// No recreation was needed, or recreation succeeded. Continue processing.
533+ Continue ,
534+ /// Plan is empty after recreation rewrite. Caller should return early.
535+ Done ,
534536}
535537
536538fn handle_recreate_requirements < F > (
537539 plan : & mut MigrationPlan ,
538540 current_models : & [ TableDef ] ,
539541 prompt_fn : F ,
540- ) -> Result < RecreateHandling >
542+ ) -> Result < RecreateOutcome >
541543where
542544 F : Fn ( & [ RecreateTableRequired ] ) -> Result < bool > ,
543545{
544546 let recreate_tables = find_non_nullable_fk_add_columns ( plan, current_models) ;
545547 if recreate_tables. is_empty ( ) {
546- return Ok ( RecreateHandling :: NotNeeded ) ;
548+ return Ok ( RecreateOutcome :: Continue ) ;
547549 }
548550
549551 if !prompt_fn ( & recreate_tables) ? {
550552 anyhow:: bail!(
551- "Migration cancelled. To proceed without recreation, make the column nullable \
552- or add it with a default value that references an existing row."
553+ "Migration cancelled. To proceed without recreation, make the column nullable or add it with a default value that references an existing row."
553554 ) ;
554555 }
555556
556557 rewrite_plan_for_recreation ( plan, & recreate_tables, current_models) ;
557558
558559 if plan. actions . is_empty ( ) {
559- return Ok ( RecreateHandling :: PlanEmptied ) ;
560+ println ! (
561+ "{} {}" ,
562+ "No changes detected." . bright_yellow( ) ,
563+ "Nothing to migrate." . bright_white( )
564+ ) ;
565+ return Ok ( RecreateOutcome :: Done ) ;
560566 }
561567
562- Ok ( RecreateHandling :: Rewritten )
568+ Ok ( RecreateOutcome :: Continue )
563569}
564570
565571pub async fn cmd_revision ( message : String , fill_with_args : Vec < String > ) -> Result < ( ) > {
@@ -580,16 +586,11 @@ pub async fn cmd_revision(message: String, fill_with_args: Vec<String>) -> Resul
580586 }
581587
582588 // Check for non-nullable FK changes that require table recreation.
583- match handle_recreate_requirements ( & mut plan, & current_models, prompt_recreate_tables) ? {
584- RecreateHandling :: NotNeeded | RecreateHandling :: Rewritten => { }
585- RecreateHandling :: PlanEmptied => {
586- println ! (
587- "{} {}" ,
588- "No changes detected." . bright_yellow( ) ,
589- "Nothing to migrate." . bright_white( )
590- ) ;
591- return Ok ( ( ) ) ;
592- }
589+ if matches ! (
590+ handle_recreate_requirements( & mut plan, & current_models, prompt_recreate_tables) ?,
591+ RecreateOutcome :: Done
592+ ) {
593+ return Ok ( ( ) ) ;
593594 }
594595
595596 // Reconstruct baseline schema for column type lookups
@@ -1311,7 +1312,7 @@ mod tests {
13111312 }
13121313
13131314 #[ test]
1314- fn handle_recreate_requirements_returns_not_needed ( ) {
1315+ fn handle_recreate_requirements_returns_continue_when_no_fk ( ) {
13151316 let mut plan = MigrationPlan {
13161317 id : String :: new ( ) ,
13171318 comment : None ,
@@ -1324,7 +1325,7 @@ mod tests {
13241325
13251326 let result = handle_recreate_requirements ( & mut plan, & [ ] , |_| Ok ( true ) ) . unwrap ( ) ;
13261327
1327- assert_eq ! ( result, RecreateHandling :: NotNeeded ) ;
1328+ assert_eq ! ( result, RecreateOutcome :: Continue ) ;
13281329 assert_eq ! ( plan. actions. len( ) , 1 ) ;
13291330 }
13301331
@@ -1376,7 +1377,7 @@ mod tests {
13761377 }
13771378
13781379 #[ test]
1379- fn handle_recreate_requirements_returns_plan_emptied_when_model_missing ( ) {
1380+ fn handle_recreate_requirements_returns_done_when_model_missing ( ) {
13801381 use vespertide_core:: { ColumnDef , ColumnType , SimpleColumnType } ;
13811382
13821383 let mut plan = MigrationPlan {
@@ -1416,10 +1417,91 @@ mod tests {
14161417
14171418 let result = handle_recreate_requirements ( & mut plan, & [ ] , |_| Ok ( true ) ) . unwrap ( ) ;
14181419
1419- assert_eq ! ( result, RecreateHandling :: PlanEmptied ) ;
1420+ assert_eq ! ( result, RecreateOutcome :: Done ) ;
14201421 assert ! ( plan. actions. is_empty( ) ) ;
14211422 }
14221423
1424+ #[ test]
1425+ fn handle_recreate_requirements_returns_continue_after_rewrite ( ) {
1426+ use vespertide_core:: { ColumnDef , ColumnType , SimpleColumnType } ;
1427+
1428+ let mut plan = MigrationPlan {
1429+ id : String :: new ( ) ,
1430+ comment : None ,
1431+ created_at : None ,
1432+ version : 1 ,
1433+ actions : vec ! [
1434+ MigrationAction :: AddColumn {
1435+ table: "post" . into( ) ,
1436+ column: Box :: new( ColumnDef {
1437+ name: "user_id" . into( ) ,
1438+ r#type: ColumnType :: Simple ( SimpleColumnType :: Uuid ) ,
1439+ nullable: false ,
1440+ default : None ,
1441+ comment: None ,
1442+ primary_key: None ,
1443+ unique: None ,
1444+ index: None ,
1445+ foreign_key: None ,
1446+ } ) ,
1447+ fill_with: None ,
1448+ } ,
1449+ MigrationAction :: AddConstraint {
1450+ table: "post" . into( ) ,
1451+ constraint: TableConstraint :: ForeignKey {
1452+ name: None ,
1453+ columns: vec![ "user_id" . into( ) ] ,
1454+ ref_table: "user" . into( ) ,
1455+ ref_columns: vec![ "id" . into( ) ] ,
1456+ on_delete: None ,
1457+ on_update: None ,
1458+ } ,
1459+ } ,
1460+ ] ,
1461+ } ;
1462+
1463+ let models = vec ! [ TableDef {
1464+ name: "post" . into( ) ,
1465+ description: None ,
1466+ columns: vec![
1467+ ColumnDef {
1468+ name: "id" . into( ) ,
1469+ r#type: ColumnType :: Simple ( SimpleColumnType :: Integer ) ,
1470+ nullable: false ,
1471+ default : None ,
1472+ comment: None ,
1473+ primary_key: None ,
1474+ unique: None ,
1475+ index: None ,
1476+ foreign_key: None ,
1477+ } ,
1478+ ColumnDef {
1479+ name: "user_id" . into( ) ,
1480+ r#type: ColumnType :: Simple ( SimpleColumnType :: Uuid ) ,
1481+ nullable: false ,
1482+ default : None ,
1483+ comment: None ,
1484+ primary_key: None ,
1485+ unique: None ,
1486+ index: None ,
1487+ foreign_key: None ,
1488+ } ,
1489+ ] ,
1490+ constraints: vec![ ] ,
1491+ } ] ;
1492+
1493+ let result = handle_recreate_requirements ( & mut plan, & models, |_| Ok ( true ) ) . unwrap ( ) ;
1494+
1495+ assert_eq ! ( result, RecreateOutcome :: Continue ) ;
1496+ assert_eq ! ( plan. actions. len( ) , 2 ) ;
1497+ assert ! (
1498+ matches!( & plan. actions[ 0 ] , MigrationAction :: DeleteTable { table } if table == "post" )
1499+ ) ;
1500+ assert ! (
1501+ matches!( & plan. actions[ 1 ] , MigrationAction :: CreateTable { table, .. } if table == "post" )
1502+ ) ;
1503+ }
1504+
14231505 #[ test]
14241506 fn test_parse_fill_with_args ( ) {
14251507 let args = vec ! [
0 commit comments