@@ -48,6 +48,7 @@ pub fn make_migrations(path: &Path, options: MigrationGeneratorOptions) -> anyho
4848
4949#[ derive( Debug , Clone , Default ) ]
5050pub struct MigrationGeneratorOptions {
51+ pub app_name : Option < String > ,
5152 pub output_dir : Option < PathBuf > ,
5253}
5354
@@ -82,6 +83,7 @@ impl MigrationGenerator {
8283 Ok ( ( ) )
8384 }
8485
86+ /// Generate migrations as a ready-to-write source code.
8587 pub fn generate_migrations_to_write (
8688 & mut self ,
8789 source_files : Vec < SourceFile > ,
@@ -95,6 +97,8 @@ impl MigrationGenerator {
9597 }
9698 }
9799
100+ /// Generate migrations and return internal structures that can be used to
101+ /// generate source code.
98102 pub fn generate_migrations (
99103 & mut self ,
100104 source_files : Vec < SourceFile > ,
@@ -319,7 +323,10 @@ impl MigrationGenerator {
319323 fn make_create_model_operation ( app_model : & ModelInSource ) -> DynOperation {
320324 DynOperation :: CreateModel {
321325 table_name : app_model. model . table_name . clone ( ) ,
322- model_ty : app_model. model . resolved_ty . clone ( ) . expect ( "resolved_ty is expected to be present when parsing the entire file with symbol resolver" ) ,
326+ model_ty : app_model. model . resolved_ty . clone ( ) . expect (
327+ "resolved_ty is expected to be present when \
328+ parsing the entire file with symbol resolver",
329+ ) ,
323330 fields : app_model. model . fields . clone ( ) ,
324331 }
325332 }
@@ -381,7 +388,10 @@ impl MigrationGenerator {
381388 fn make_add_field_operation ( app_model : & ModelInSource , field : & Field ) -> DynOperation {
382389 DynOperation :: AddField {
383390 table_name : app_model. model . table_name . clone ( ) ,
384- model_ty : app_model. model . resolved_ty . clone ( ) . expect ( "resolved_ty is expected to be present when parsing the entire file with symbol resolver" ) ,
391+ model_ty : app_model. model . resolved_ty . clone ( ) . expect (
392+ "resolved_ty is expected to be present \
393+ when parsing the entire file with symbol resolver",
394+ ) ,
385395 field : field. clone ( ) ,
386396 }
387397 }
@@ -426,7 +436,7 @@ impl MigrationGenerator {
426436 . map ( |dependency| dependency. repr ( ) )
427437 . collect ( ) ;
428438
429- let app_name = & self . crate_name ;
439+ let app_name = self . options . app_name . as_ref ( ) . unwrap_or ( & self . crate_name ) ;
430440 let migration_name = & migration. migration_name ;
431441 let migration_def = quote ! {
432442 #[ derive( Debug , Copy , Clone ) ]
@@ -666,6 +676,8 @@ pub struct GeneratedMigration {
666676}
667677
668678impl GeneratedMigration {
679+ /// Get the list of [`DynDependency`] for all foreign keys that point
680+ /// to models that are **not** created in this migration.
669681 fn get_foreign_key_dependencies ( & self ) -> Vec < DynDependency > {
670682 let create_ops = self . get_create_ops_map ( ) ;
671683 let ops_adding_foreign_keys = self . get_ops_adding_foreign_keys ( ) ;
@@ -682,6 +694,16 @@ impl GeneratedMigration {
682694 dependencies
683695 }
684696
697+ /// Removes dependency cycles by removing operations that create cycles.
698+ ///
699+ /// This method tries to minimize the number of operations added by
700+ /// calculating the minimum feedback arc set of the dependency graph.
701+ ///
702+ /// This method modifies the `operations` field in place.
703+ ///
704+ /// # See also
705+ ///
706+ /// * [`Self::remove_dependency`]
685707 fn remove_cycles ( & mut self ) {
686708 let graph = self . construct_dependency_graph ( ) ;
687709
@@ -701,6 +723,11 @@ impl GeneratedMigration {
701723 }
702724 }
703725
726+ /// Remove a dependency between two operations.
727+ ///
728+ /// This is done by removing foreign keys from the `from` operation that
729+ /// point to the model created by `to` operation, and creating a new
730+ /// `AddField` operation for each removed foreign key.
704731 #[ must_use]
705732 fn remove_dependency ( from : & mut DynOperation , to : & DynOperation ) -> Vec < DynOperation > {
706733 match from {
@@ -712,7 +739,10 @@ impl GeneratedMigration {
712739 let to_type = match to {
713740 DynOperation :: CreateModel { model_ty, .. } => model_ty,
714741 DynOperation :: AddField { .. } => {
715- unreachable ! ( "AddField operation shouldn't be a dependency of CreateModel because it doesn't create a new model" )
742+ unreachable ! (
743+ "AddField operation shouldn't be a dependency of CreateModel \
744+ because it doesn't create a new model"
745+ )
716746 }
717747 } ;
718748 trace ! (
@@ -745,18 +775,36 @@ impl GeneratedMigration {
745775 }
746776 }
747777
778+ /// Topologically sort operations in this migration.
779+ ///
780+ /// This is to ensure that operations will be applied in the correct order.
781+ /// If there are no dependencies between operations, the order of operations
782+ /// will not be modified.
783+ ///
784+ /// This method modifies the `operations` field in place.
785+ ///
786+ /// # Panics
787+ ///
788+ /// This method should be called after removing cycles; otherwise it will
789+ /// panic.
748790 fn toposort_operations ( & mut self ) {
749791 let graph = self . construct_dependency_graph ( ) ;
750792
751793 let sorted = petgraph:: algo:: toposort ( & graph, None )
752794 . expect ( "cycles shouldn't exist after removing them" ) ;
753795 let mut sorted = sorted
754796 . into_iter ( )
755- . map ( petgraph:: prelude :: NodeIndex :: index)
797+ . map ( petgraph:: graph :: NodeIndex :: index)
756798 . collect :: < Vec < _ > > ( ) ;
757799 flareon:: __private:: apply_permutation ( & mut self . operations , & mut sorted) ;
758800 }
759801
802+ /// Construct a graph that represents reverse dependencies between
803+ /// operations in this migration.
804+ ///
805+ /// The graph is directed and has an edge from operation A to operation B
806+ /// if operation B creates a foreign key that points to a model created by
807+ /// operation A.
760808 #[ must_use]
761809 fn construct_dependency_graph ( & mut self ) -> DiGraph < usize , ( ) , usize > {
762810 let create_ops = self . get_create_ops_map ( ) ;
@@ -769,7 +817,11 @@ impl GeneratedMigration {
769817 }
770818 for ( i, dependency_ty) in & ops_adding_foreign_keys {
771819 if let Some ( & dependency) = create_ops. get ( dependency_ty) {
772- graph. add_edge ( NodeIndex :: new ( dependency) , NodeIndex :: new ( * i) , ( ) ) ;
820+ graph. add_edge (
821+ petgraph:: graph:: NodeIndex :: new ( dependency) ,
822+ petgraph:: graph:: NodeIndex :: new ( * i) ,
823+ ( ) ,
824+ ) ;
773825 }
774826 }
775827
@@ -855,16 +907,19 @@ impl Repr for Field {
855907 let mut tokens = quote ! {
856908 :: flareon:: db:: migrations:: Field :: new( :: flareon:: db:: Identifier :: new( #column_name) , <#ty as :: flareon:: db:: DatabaseField >:: TYPE )
857909 } ;
858- if self
859- . auto_value
860- . expect ( "auto_value is expected to be present when parsing the entire file with symbol resolver")
861- {
910+ if self . auto_value . expect (
911+ " auto_value is expected to be present \
912+ when parsing the entire file with symbol resolver",
913+ ) {
862914 tokens = quote ! { #tokens. auto( ) }
863915 }
864916 if self . primary_key {
865917 tokens = quote ! { #tokens. primary_key( ) }
866918 }
867- if let Some ( fk_spec) = self . foreign_key . clone ( ) . expect ( "foreign_key is expected to be present when parsing the entire file with symbol resolver" ) {
919+ if let Some ( fk_spec) = self . foreign_key . clone ( ) . expect (
920+ "foreign_key is expected to be present \
921+ when parsing the entire file with symbol resolver",
922+ ) {
868923 let to_model = & fk_spec. to_model ;
869924
870925 tokens = quote ! {
@@ -966,7 +1021,8 @@ fn is_field_foreign_key_to(field: &Field, ty: &syn::Type) -> bool {
9661021/// Returns [`None`] if the field is not a foreign key.
9671022fn foreign_key_for_field ( field : & Field ) -> Option < syn:: Type > {
9681023 match field. foreign_key . clone ( ) . expect (
969- "foreign_key is expected to be present when parsing the entire file with symbol resolver" ,
1024+ "foreign_key is expected to be present \
1025+ when parsing the entire file with symbol resolver",
9701026 ) {
9711027 None => None ,
9721028 Some ( foreign_key_spec) => Some ( foreign_key_spec. to_model ) ,
@@ -1339,4 +1395,59 @@ mod tests {
13391395 model_type: parse_quote!( crate :: Table4 ) ,
13401396 } ) ) ;
13411397 }
1398+
1399+ #[ test]
1400+ fn make_add_field_operation ( ) {
1401+ let app_model = ModelInSource {
1402+ model_item : parse_quote ! {
1403+ struct TestModel {
1404+ id: i32 ,
1405+ field1: i32 ,
1406+ }
1407+ } ,
1408+ model : Model {
1409+ name : format_ident ! ( "TestModel" ) ,
1410+ original_name : "TestModel" . to_string ( ) ,
1411+ resolved_ty : Some ( parse_quote ! ( TestModel ) ) ,
1412+ model_type : Default :: default ( ) ,
1413+ table_name : "test_model" . to_string ( ) ,
1414+ pk_field : Field {
1415+ field_name : format_ident ! ( "id" ) ,
1416+ column_name : "id" . to_string ( ) ,
1417+ ty : parse_quote ! ( i32 ) ,
1418+ auto_value : MaybeUnknown :: Known ( true ) ,
1419+ primary_key : true ,
1420+ unique : false ,
1421+ foreign_key : MaybeUnknown :: Known ( None ) ,
1422+ } ,
1423+ fields : vec ! [ ] ,
1424+ } ,
1425+ } ;
1426+
1427+ let field = Field {
1428+ field_name : format_ident ! ( "new_field" ) ,
1429+ column_name : "new_field" . to_string ( ) ,
1430+ ty : parse_quote ! ( i32 ) ,
1431+ auto_value : MaybeUnknown :: Known ( false ) ,
1432+ primary_key : false ,
1433+ unique : false ,
1434+ foreign_key : MaybeUnknown :: Known ( None ) ,
1435+ } ;
1436+
1437+ let operation = MigrationGenerator :: make_add_field_operation ( & app_model, & field) ;
1438+
1439+ match operation {
1440+ DynOperation :: AddField {
1441+ table_name,
1442+ model_ty,
1443+ field : op_field,
1444+ } => {
1445+ assert_eq ! ( table_name, "test_model" ) ;
1446+ assert_eq ! ( model_ty, parse_quote!( TestModel ) ) ;
1447+ assert_eq ! ( op_field. column_name, "new_field" ) ;
1448+ assert_eq ! ( op_field. ty, parse_quote!( i32 ) ) ;
1449+ }
1450+ _ => panic ! ( "Expected AddField operation" ) ,
1451+ }
1452+ }
13421453}
0 commit comments