@@ -350,8 +350,16 @@ pub trait LanguagePlugin {
350350 }
351351 }
352352
353- // If there's exactly one folder at the root and no files, use it
354- if folders. len ( ) == 1 && root_file_count == 0 {
353+ // If there's exactly one folder at the root and no files, skip over it
354+ // Special case: don't skip over certain folders
355+ let excluded_folders = [ "src" ] ;
356+ if folders. len ( ) == 1
357+ && root_file_count == 0
358+ && !folders[ 0 ]
359+ . file_name ( )
360+ . map ( |name| excluded_folders. contains ( & name. to_string_lossy ( ) . as_ref ( ) ) )
361+ . unwrap_or ( false )
362+ {
355363 return folders[ 0 ] . clone ( ) ;
356364 }
357365 }
@@ -1191,4 +1199,30 @@ force_update:
11911199 . unwrap ( ) ;
11921200 assert_eq ! ( content, "content" ) ;
11931201 }
1202+
1203+ #[ test]
1204+ fn safe_find_project_dir_does_not_skip_over_src_folder ( ) {
1205+ init ( ) ;
1206+
1207+ let temp = tempfile:: tempdir ( ) . unwrap ( ) ;
1208+ // root has only a single "src" folder, no root files
1209+ // SimpleMockPlugin will never find project directory, so fallback logic will be used
1210+ file_to ( & temp, "src/student_file" , "content" ) ;
1211+ let zip = dir_to_zip ( & temp) ;
1212+
1213+ // extract student files - should use the "src" folder as project root
1214+ SimpleMockPlugin :: extract_student_files (
1215+ std:: io:: Cursor :: new ( zip) ,
1216+ Compression :: Zip ,
1217+ & temp. path ( ) . join ( "extracted" ) ,
1218+ )
1219+ . unwrap ( ) ;
1220+
1221+ // The file should be extracted as student_file (using the src folder as project root)
1222+ // This test verifies that the "src" folder is used as the project directory
1223+ assert ! ( temp. path( ) . join( "extracted/src/student_file" ) . exists( ) ) ;
1224+ let content =
1225+ std:: fs:: read_to_string ( temp. path ( ) . join ( "extracted/src/student_file" ) ) . unwrap ( ) ;
1226+ assert_eq ! ( content, "content" ) ;
1227+ }
11941228}
0 commit comments