@@ -4,7 +4,7 @@ use std::fs;
44use anyhow:: { Context , Result } ;
55use chrono:: Utc ;
66use colored:: Colorize ;
7- use dialoguer:: Input ;
7+ use dialoguer:: { Input , Select } ;
88use serde_json:: Value ;
99use vespertide_config:: FileFormat ;
1010use vespertide_core:: { MigrationAction , MigrationPlan } ;
@@ -118,15 +118,32 @@ fn prompt_fill_with_value(prompt: &str) -> Result<String> {
118118 Ok ( wrap_if_spaces ( value) )
119119}
120120
121+ /// Prompt the user to select an enum value using dialoguer Select.
122+ /// Returns the selected value wrapped in single quotes for SQL.
123+ #[ cfg( not( tarpaulin_include) ) ]
124+ fn prompt_enum_value ( prompt : & str , enum_values : & [ String ] ) -> Result < String > {
125+ let selection = Select :: new ( )
126+ . with_prompt ( prompt)
127+ . items ( enum_values)
128+ . default ( 0 )
129+ . interact ( )
130+ . context ( "failed to read selection" ) ?;
131+ // Return the selected value with single quotes for SQL enum literal
132+ Ok ( format ! ( "'{}'" , enum_values[ selection] ) )
133+ }
134+
121135/// Collect fill_with values interactively for missing columns.
122136/// The `prompt_fn` parameter allows injecting a mock for testing.
123- fn collect_fill_with_values < F > (
137+ /// The `enum_prompt_fn` parameter handles enum type columns with selection UI.
138+ fn collect_fill_with_values < F , E > (
124139 missing : & [ vespertide_planner:: FillWithRequired ] ,
125140 fill_values : & mut HashMap < ( String , String ) , String > ,
126141 prompt_fn : F ,
142+ enum_prompt_fn : E ,
127143) -> Result < ( ) >
128144where
129145 F : Fn ( & str ) -> Result < String > ,
146+ E : Fn ( & str , & [ String ] ) -> Result < String > ,
130147{
131148 print_fill_with_header ( ) ;
132149
@@ -139,7 +156,13 @@ where
139156 item. action_type ,
140157 ) ;
141158
142- let value = prompt_fn ( & prompt) ?;
159+ let value = if let Some ( enum_values) = & item. enum_values {
160+ // Use selection UI for enum types
161+ enum_prompt_fn ( & prompt, enum_values) ?
162+ } else {
163+ // Use text input for other types
164+ prompt_fn ( & prompt) ?
165+ } ;
143166 fill_values. insert ( ( item. table . clone ( ) , item. column . clone ( ) ) , value) ;
144167 }
145168
@@ -184,18 +207,20 @@ fn apply_fill_with_to_plan(
184207
185208/// Handle interactive fill_with collection if there are missing values.
186209/// Returns the updated fill_values map after collecting from user.
187- fn handle_missing_fill_with < F > (
210+ fn handle_missing_fill_with < F , E > (
188211 plan : & mut MigrationPlan ,
189212 fill_values : & mut HashMap < ( String , String ) , String > ,
190213 prompt_fn : F ,
214+ enum_prompt_fn : E ,
191215) -> Result < ( ) >
192216where
193217 F : Fn ( & str ) -> Result < String > ,
218+ E : Fn ( & str , & [ String ] ) -> Result < String > ,
194219{
195220 let missing = find_missing_fill_with ( plan) ;
196221
197222 if !missing. is_empty ( ) {
198- collect_fill_with_values ( & missing, fill_values, prompt_fn) ?;
223+ collect_fill_with_values ( & missing, fill_values, prompt_fn, enum_prompt_fn ) ?;
199224
200225 // Apply the collected fill_with values
201226 apply_fill_with_to_plan ( plan, fill_values) ;
@@ -228,7 +253,12 @@ pub fn cmd_revision(message: String, fill_with_args: Vec<String>) -> Result<()>
228253 apply_fill_with_to_plan ( & mut plan, & fill_values) ;
229254
230255 // Handle any missing fill_with values interactively
231- handle_missing_fill_with ( & mut plan, & mut fill_values, prompt_fill_with_value) ?;
256+ handle_missing_fill_with (
257+ & mut plan,
258+ & mut fill_values,
259+ prompt_fill_with_value,
260+ prompt_enum_value,
261+ ) ?;
232262
233263 plan. comment = Some ( message) ;
234264 if plan. created_at . is_none ( ) {
@@ -838,6 +868,11 @@ mod tests {
838868 print_fill_with_footer ( ) ;
839869 }
840870
871+ // Mock enum prompt function for tests - returns first enum value quoted
872+ fn mock_enum_prompt ( _prompt : & str , values : & [ String ] ) -> Result < String > {
873+ Ok ( format ! ( "'{}'" , values[ 0 ] ) )
874+ }
875+
841876 #[ test]
842877 fn test_collect_fill_with_values_single_item ( ) {
843878 use vespertide_planner:: FillWithRequired ;
@@ -849,6 +884,7 @@ mod tests {
849884 action_type: "AddColumn" ,
850885 column_type: Some ( "text" . to_string( ) ) ,
851886 default_value: Some ( "''" . to_string( ) ) ,
887+ enum_values: None ,
852888 } ] ;
853889
854890 let mut fill_values = HashMap :: new ( ) ;
@@ -857,7 +893,8 @@ mod tests {
857893 let mock_prompt =
858894 |_prompt : & str | -> Result < String > { Ok ( "'test@example.com'" . to_string ( ) ) } ;
859895
860- let result = collect_fill_with_values ( & missing, & mut fill_values, mock_prompt) ;
896+ let result =
897+ collect_fill_with_values ( & missing, & mut fill_values, mock_prompt, mock_enum_prompt) ;
861898 assert ! ( result. is_ok( ) ) ;
862899 assert_eq ! ( fill_values. len( ) , 1 ) ;
863900 assert_eq ! (
@@ -878,6 +915,7 @@ mod tests {
878915 action_type: "AddColumn" ,
879916 column_type: Some ( "text" . to_string( ) ) ,
880917 default_value: Some ( "''" . to_string( ) ) ,
918+ enum_values: None ,
881919 } ,
882920 FillWithRequired {
883921 action_index: 1 ,
@@ -886,6 +924,7 @@ mod tests {
886924 action_type: "ModifyColumnNullable" ,
887925 column_type: None ,
888926 default_value: None ,
927+ enum_values: None ,
889928 } ,
890929 ] ;
891930
@@ -903,7 +942,8 @@ mod tests {
903942 }
904943 } ;
905944
906- let result = collect_fill_with_values ( & missing, & mut fill_values, mock_prompt) ;
945+ let result =
946+ collect_fill_with_values ( & missing, & mut fill_values, mock_prompt, mock_enum_prompt) ;
907947 assert ! ( result. is_ok( ) ) ;
908948 assert_eq ! ( fill_values. len( ) , 2 ) ;
909949 assert_eq ! (
@@ -930,7 +970,8 @@ mod tests {
930970
931971 // Note: The function still prints header/footer even for empty list
932972 // This is a design choice - in practice, cmd_revision won't call this with empty list
933- let result = collect_fill_with_values ( & missing, & mut fill_values, mock_prompt) ;
973+ let result =
974+ collect_fill_with_values ( & missing, & mut fill_values, mock_prompt, mock_enum_prompt) ;
934975 assert ! ( result. is_ok( ) ) ;
935976 assert ! ( fill_values. is_empty( ) ) ;
936977 }
@@ -946,6 +987,7 @@ mod tests {
946987 action_type: "AddColumn" ,
947988 column_type: Some ( "text" . to_string( ) ) ,
948989 default_value: Some ( "''" . to_string( ) ) ,
990+ enum_values: None ,
949991 } ] ;
950992
951993 let mut fill_values = HashMap :: new ( ) ;
@@ -954,7 +996,8 @@ mod tests {
954996 let mock_prompt =
955997 |_prompt : & str | -> Result < String > { Err ( anyhow:: anyhow!( "input cancelled" ) ) } ;
956998
957- let result = collect_fill_with_values ( & missing, & mut fill_values, mock_prompt) ;
999+ let result =
1000+ collect_fill_with_values ( & missing, & mut fill_values, mock_prompt, mock_enum_prompt) ;
9581001 assert ! ( result. is_err( ) ) ;
9591002 assert ! ( fill_values. is_empty( ) ) ;
9601003 }
@@ -998,7 +1041,8 @@ mod tests {
9981041 let mock_prompt =
9991042 |_prompt : & str | -> Result < String > { Ok ( "'test@example.com'" . to_string ( ) ) } ;
10001043
1001- let result = handle_missing_fill_with ( & mut plan, & mut fill_values, mock_prompt) ;
1044+ let result =
1045+ handle_missing_fill_with ( & mut plan, & mut fill_values, mock_prompt, mock_enum_prompt) ;
10021046 assert ! ( result. is_ok( ) ) ;
10031047
10041048 // Verify fill_with was applied to the plan
@@ -1049,7 +1093,8 @@ mod tests {
10491093 panic ! ( "Should not be called when no missing fill_with values" ) ;
10501094 } ;
10511095
1052- let result = handle_missing_fill_with ( & mut plan, & mut fill_values, mock_prompt) ;
1096+ let result =
1097+ handle_missing_fill_with ( & mut plan, & mut fill_values, mock_prompt, mock_enum_prompt) ;
10531098 assert ! ( result. is_ok( ) ) ;
10541099 assert ! ( fill_values. is_empty( ) ) ;
10551100 }
@@ -1085,7 +1130,8 @@ mod tests {
10851130 let mock_prompt =
10861131 |_prompt : & str | -> Result < String > { Err ( anyhow:: anyhow!( "user cancelled" ) ) } ;
10871132
1088- let result = handle_missing_fill_with ( & mut plan, & mut fill_values, mock_prompt) ;
1133+ let result =
1134+ handle_missing_fill_with ( & mut plan, & mut fill_values, mock_prompt, mock_enum_prompt) ;
10891135 assert ! ( result. is_err( ) ) ;
10901136
10911137 // Plan should not be modified on error
@@ -1144,7 +1190,8 @@ mod tests {
11441190 }
11451191 } ;
11461192
1147- let result = handle_missing_fill_with ( & mut plan, & mut fill_values, mock_prompt) ;
1193+ let result =
1194+ handle_missing_fill_with ( & mut plan, & mut fill_values, mock_prompt, mock_enum_prompt) ;
11481195 assert ! ( result. is_ok( ) ) ;
11491196
11501197 // Verify both actions were updated
@@ -1163,6 +1210,46 @@ mod tests {
11631210 }
11641211 }
11651212
1213+ #[ test]
1214+ fn test_collect_fill_with_values_enum_column ( ) {
1215+ use vespertide_planner:: FillWithRequired ;
1216+
1217+ let missing = vec ! [ FillWithRequired {
1218+ action_index: 0 ,
1219+ table: "orders" . to_string( ) ,
1220+ column: "status" . to_string( ) ,
1221+ action_type: "AddColumn" ,
1222+ column_type: Some ( "enum<order_status>" . to_string( ) ) ,
1223+ default_value: None ,
1224+ enum_values: Some ( vec![
1225+ "pending" . to_string( ) ,
1226+ "confirmed" . to_string( ) ,
1227+ "shipped" . to_string( ) ,
1228+ ] ) ,
1229+ } ] ;
1230+
1231+ let mut fill_values = HashMap :: new ( ) ;
1232+
1233+ // Mock prompt function that should NOT be called for enum columns
1234+ let mock_prompt = |_prompt : & str | -> Result < String > {
1235+ panic ! ( "Should not be called for enum columns" ) ;
1236+ } ;
1237+
1238+ // Mock enum prompt that selects the second value
1239+ let mock_enum = |_prompt : & str , values : & [ String ] | -> Result < String > {
1240+ // Select "confirmed" (index 1)
1241+ Ok ( format ! ( "'{}'" , values[ 1 ] ) )
1242+ } ;
1243+
1244+ let result = collect_fill_with_values ( & missing, & mut fill_values, mock_prompt, mock_enum) ;
1245+ assert ! ( result. is_ok( ) ) ;
1246+ assert_eq ! ( fill_values. len( ) , 1 ) ;
1247+ assert_eq ! (
1248+ fill_values. get( & ( "orders" . to_string( ) , "status" . to_string( ) ) ) ,
1249+ Some ( & "'confirmed'" . to_string( ) )
1250+ ) ;
1251+ }
1252+
11661253 #[ test]
11671254 fn test_wrap_if_spaces_empty ( ) {
11681255 assert_eq ! ( wrap_if_spaces( "" . to_string( ) ) , "" ) ;
0 commit comments