@@ -15,6 +15,7 @@ pub enum OrmArg {
1515 Seaorm ,
1616 Sqlalchemy ,
1717 Sqlmodel ,
18+ Jpa ,
1819}
1920
2021impl From < OrmArg > for Orm {
@@ -23,6 +24,7 @@ impl From<OrmArg> for Orm {
2324 OrmArg :: Seaorm => Orm :: SeaOrm ,
2425 OrmArg :: Sqlalchemy => Orm :: SqlAlchemy ,
2526 OrmArg :: Sqlmodel => Orm :: SqlModel ,
27+ OrmArg :: Jpa => Orm :: Jpa ,
2628 }
2729 }
2830}
@@ -133,6 +135,7 @@ async fn clean_export_dir(root: &Path, orm: Orm) -> Result<()> {
133135 let ext = match orm {
134136 Orm :: SeaOrm => "rs" ,
135137 Orm :: SqlAlchemy | Orm :: SqlModel => "py" ,
138+ Orm :: Jpa => "java" ,
136139 } ;
137140
138141 clean_dir_recursive ( root, ext) . await ?;
@@ -224,13 +227,32 @@ fn build_output_path(root: &Path, rel_path: &Path, orm: Orm) -> PathBuf {
224227 let ext = match orm {
225228 Orm :: SeaOrm => "rs" ,
226229 Orm :: SqlAlchemy | Orm :: SqlModel => "py" ,
230+ Orm :: Jpa => "java" ,
227231 } ;
228- out. set_file_name ( format ! ( "{}.{}" , sanitized, ext) ) ;
232+ // Java requires filename to match PascalCase class name
233+ let file_stem = if matches ! ( orm, Orm :: Jpa ) {
234+ to_pascal_case ( & sanitized)
235+ } else {
236+ sanitized
237+ } ;
238+ out. set_file_name ( format ! ( "{}.{}" , file_stem, ext) ) ;
229239 }
230240
231241 out
232242}
233243
244+ fn to_pascal_case ( s : & str ) -> String {
245+ s. split ( '_' )
246+ . map ( |word| {
247+ let mut chars = word. chars ( ) ;
248+ match chars. next ( ) {
249+ None => String :: new ( ) ,
250+ Some ( first) => first. to_uppercase ( ) . chain ( chars) . collect ( ) ,
251+ }
252+ } )
253+ . collect ( )
254+ }
255+
234256fn sanitize_filename ( name : & str ) -> String {
235257 name. chars ( )
236258 . map ( |ch| {
@@ -581,6 +603,7 @@ mod tests {
581603 #[ case( OrmArg :: Seaorm , Orm :: SeaOrm ) ]
582604 #[ case( OrmArg :: Sqlalchemy , Orm :: SqlAlchemy ) ]
583605 #[ case( OrmArg :: Sqlmodel , Orm :: SqlModel ) ]
606+ #[ case( OrmArg :: Jpa , Orm :: Jpa ) ]
584607 fn orm_arg_maps_to_enum ( #[ case] arg : OrmArg , #[ case] expected : Orm ) {
585608 assert_eq ! ( Orm :: from( arg) , expected) ;
586609 }
@@ -745,6 +768,23 @@ mod tests {
745768 assert ! ( !root. join( "model.py" ) . exists( ) ) ;
746769 }
747770
771+ #[ tokio:: test]
772+ async fn clean_export_dir_removes_java_files_for_jpa ( ) {
773+ let tmp = tempdir ( ) . unwrap ( ) ;
774+ let root = tmp. path ( ) . join ( "export_dir" ) ;
775+ std_fs:: create_dir_all ( & root) . unwrap ( ) ;
776+
777+ std_fs:: write ( root. join ( "User.java" ) , "// java entity" ) . unwrap ( ) ;
778+ std_fs:: write ( root. join ( "Order.java" ) , "// java entity" ) . unwrap ( ) ;
779+ std_fs:: write ( root. join ( "keep.rs" ) , "// keep this" ) . unwrap ( ) ;
780+
781+ clean_export_dir ( & root, Orm :: Jpa ) . await . unwrap ( ) ;
782+
783+ assert ! ( !root. join( "User.java" ) . exists( ) ) ;
784+ assert ! ( !root. join( "Order.java" ) . exists( ) ) ;
785+ assert ! ( root. join( "keep.rs" ) . exists( ) ) ;
786+ }
787+
748788 #[ tokio:: test]
749789 async fn clean_export_dir_handles_missing_directory ( ) {
750790 let tmp = tempdir ( ) . unwrap ( ) ;
@@ -806,4 +846,45 @@ mod tests {
806846 let result = clean_dir_recursive ( & file_path, "rs" ) . await ;
807847 assert ! ( result. is_ok( ) ) ;
808848 }
849+
850+ #[ test]
851+ fn build_output_path_jpa_uses_pascal_case_java_extension ( ) {
852+ use std:: path:: Path ;
853+ let root = Path :: new ( "src/models" ) ;
854+
855+ // snake_case model → PascalCase .java
856+ let rel_path = Path :: new ( "order_item.json" ) ;
857+ let out = build_output_path ( root, rel_path, Orm :: Jpa ) ;
858+ assert_eq ! ( out, Path :: new( "src/models/OrderItem.java" ) ) ;
859+
860+ // Single word
861+ let rel_path2 = Path :: new ( "users.json" ) ;
862+ let out2 = build_output_path ( root, rel_path2, Orm :: Jpa ) ;
863+ assert_eq ! ( out2, Path :: new( "src/models/Users.java" ) ) ;
864+
865+ // Nested path
866+ let rel_path3 = Path :: new ( "blog/post_comment.yaml" ) ;
867+ let out3 = build_output_path ( root, rel_path3, Orm :: Jpa ) ;
868+ assert_eq ! ( out3, Path :: new( "src/models/blog/PostComment.java" ) ) ;
869+ }
870+
871+ #[ test]
872+ fn build_output_path_jpa_strips_vespertide_suffix ( ) {
873+ use std:: path:: Path ;
874+ let root = Path :: new ( "src/models" ) ;
875+
876+ let rel_path = Path :: new ( "user.vespertide.json" ) ;
877+ let out = build_output_path ( root, rel_path, Orm :: Jpa ) ;
878+ assert_eq ! ( out, Path :: new( "src/models/User.java" ) ) ;
879+ }
880+
881+ #[ rstest]
882+ #[ case( "order_item" , "OrderItem" ) ]
883+ #[ case( "users" , "Users" ) ]
884+ #[ case( "a" , "A" ) ]
885+ #[ case( "user_profile_image" , "UserProfileImage" ) ]
886+ #[ case( "a__b" , "AB" ) ]
887+ fn test_to_pascal_case ( #[ case] input : & str , #[ case] expected : & str ) {
888+ assert_eq ! ( to_pascal_case( input) , expected) ;
889+ }
809890}
0 commit comments