@@ -215,38 +215,80 @@ pub fn delete_path_sync<P: AsRef<Path>>(path: P) -> io::Result<()> {
215215 delete_path ( path) . map ( |_| ( ) )
216216}
217217
218+ #[ cfg( windows) ]
219+ fn strip_windows_extended_prefix ( path : & Path ) -> Option < PathBuf > {
220+ let raw = path. to_string_lossy ( ) ;
221+
222+ if let Some ( stripped) = raw. strip_prefix ( r"\\?\UNC\" ) {
223+ return Some ( PathBuf :: from ( format ! ( r"\\{}" , stripped. replace( '/' , "\\ " ) ) ) ) ;
224+ }
225+
226+ if let Some ( stripped) = raw. strip_prefix ( r"\\?\" ) {
227+ return Some ( PathBuf :: from ( stripped. replace ( '/' , "\\ " ) ) ) ;
228+ }
229+
230+ if let Some ( stripped) = raw. strip_prefix ( "//?/UNC/" ) {
231+ return Some ( PathBuf :: from ( format ! ( r"\\{}" , stripped. replace( '/' , "\\ " ) ) ) ) ;
232+ }
233+
234+ if let Some ( stripped) = raw. strip_prefix ( "//?/" ) {
235+ return Some ( PathBuf :: from ( stripped. replace ( '/' , "\\ " ) ) ) ;
236+ }
237+
238+ None
239+ }
240+
241+ fn resolve_delete_path ( path : & Path ) -> io:: Result < Option < ( PathBuf , fs:: Metadata ) > > {
242+ match fs:: symlink_metadata ( path) {
243+ Ok ( metadata) => Ok ( Some ( ( path. to_path_buf ( ) , metadata) ) ) ,
244+ Err ( err) if err. kind ( ) == io:: ErrorKind :: NotFound => {
245+ #[ cfg( windows) ]
246+ if let Some ( fallback) = strip_windows_extended_prefix ( path)
247+ && fallback != path
248+ {
249+ return match fs:: symlink_metadata ( & fallback) {
250+ Ok ( metadata) => Ok ( Some ( ( fallback, metadata) ) ) ,
251+ Err ( fallback_err) if fallback_err. kind ( ) == io:: ErrorKind :: NotFound => Ok ( None ) ,
252+ Err ( fallback_err) => Err ( fallback_err) ,
253+ } ;
254+ }
255+
256+ Ok ( None )
257+ }
258+ Err ( err) => Err ( err) ,
259+ }
260+ }
261+
218262fn delete_path ( path : impl AsRef < Path > ) -> io:: Result < bool > {
219- let path = path. as_ref ( ) ;
220- let metadata = match fs:: symlink_metadata ( path) {
221- Ok ( metadata) => metadata,
222- Err ( err) if err. kind ( ) == io:: ErrorKind :: NotFound => return Ok ( false ) ,
223- Err ( err) => return Err ( err) ,
263+ let ( path, metadata) = match resolve_delete_path ( path. as_ref ( ) ) ? {
264+ Some ( resolved) => resolved,
265+ None => return Ok ( false ) ,
224266 } ;
225267
226268 if metadata. file_type ( ) . is_symlink ( ) {
227269 #[ cfg( windows) ]
228270 {
229- return fs:: metadata ( path)
271+ return fs:: metadata ( & path)
230272 . map ( |resolved| resolved. is_dir ( ) )
231273 . unwrap_or ( false )
232- . then ( || fs:: remove_dir ( path) . or_else ( |_| fs:: remove_file ( path) ) )
233- . unwrap_or_else ( || fs:: remove_file ( path) . or_else ( |_| fs:: remove_dir ( path) ) )
274+ . then ( || fs:: remove_dir ( & path) . or_else ( |_| fs:: remove_file ( & path) ) )
275+ . unwrap_or_else ( || fs:: remove_file ( & path) . or_else ( |_| fs:: remove_dir ( & path) ) )
234276 . map ( |_| true ) ;
235277 }
236278 #[ cfg( not( windows) ) ]
237279 {
238- return fs:: remove_file ( path) . map ( |_| true ) ;
280+ return fs:: remove_file ( & path) . map ( |_| true ) ;
239281 }
240282 }
241283
242284 if metadata. is_dir ( ) {
243- match fs:: remove_dir_all ( path) {
285+ match fs:: remove_dir_all ( & path) {
244286 Ok ( ( ) ) => Ok ( true ) ,
245287 Err ( err) if err. kind ( ) == io:: ErrorKind :: NotFound => Ok ( false ) ,
246288 Err ( err) => Err ( err) ,
247289 }
248290 } else {
249- match fs:: remove_file ( path) {
291+ match fs:: remove_file ( & path) {
250292 Ok ( ( ) ) => Ok ( true ) ,
251293 Err ( err) if err. kind ( ) == io:: ErrorKind :: NotFound => Ok ( false ) ,
252294 Err ( err) => Err ( err) ,
@@ -293,18 +335,16 @@ pub struct DeleteTargetsResult {
293335}
294336
295337fn delete_empty_directory ( path : impl AsRef < Path > ) -> io:: Result < bool > {
296- let path = path. as_ref ( ) ;
297- let metadata = match fs:: symlink_metadata ( path) {
298- Ok ( metadata) => metadata,
299- Err ( err) if err. kind ( ) == io:: ErrorKind :: NotFound => return Ok ( false ) ,
300- Err ( err) => return Err ( err) ,
338+ let ( path, metadata) = match resolve_delete_path ( path. as_ref ( ) ) ? {
339+ Some ( resolved) => resolved,
340+ None => return Ok ( false ) ,
301341 } ;
302342
303343 if metadata. file_type ( ) . is_symlink ( ) || !metadata. is_dir ( ) {
304344 return Ok ( false ) ;
305345 }
306346
307- match fs:: remove_dir ( path) {
347+ match fs:: remove_dir ( & path) {
308348 Ok ( ( ) ) => Ok ( true ) ,
309349 Err ( err)
310350 if err. kind ( ) == io:: ErrorKind :: NotFound
@@ -708,6 +748,36 @@ mod tests {
708748 assert ! ( target_dir. exists( ) ) ;
709749 }
710750
751+ #[ cfg( windows) ]
752+ #[ test]
753+ fn delete_files_handles_windows_extended_prefix_paths ( ) {
754+ let dir = tempdir ( ) . unwrap ( ) ;
755+ let file = dir. path ( ) . join ( "artifact.txt" ) ;
756+ fs:: write ( & file, b"data" ) . unwrap ( ) ;
757+
758+ let extended = format ! ( r"\\?\{}" , file. display( ) ) ;
759+ let result = delete_files ( & [ extended] ) ;
760+
761+ assert_eq ! ( result. deleted, 1 ) ;
762+ assert ! ( result. errors. is_empty( ) ) ;
763+ assert ! ( !file. exists( ) ) ;
764+ }
765+
766+ #[ cfg( windows) ]
767+ #[ test]
768+ fn delete_files_handles_windows_slash_extended_prefix_paths ( ) {
769+ let dir = tempdir ( ) . unwrap ( ) ;
770+ let file = dir. path ( ) . join ( "artifact.txt" ) ;
771+ fs:: write ( & file, b"data" ) . unwrap ( ) ;
772+
773+ let slash_extended = format ! ( "//?/{}" , file. display( ) . to_string( ) . replace( '\\' , "/" ) ) ;
774+ let result = delete_files ( & [ slash_extended] ) ;
775+
776+ assert_eq ! ( result. deleted, 1 ) ;
777+ assert ! ( result. errors. is_empty( ) ) ;
778+ assert ! ( !file. exists( ) ) ;
779+ }
780+
711781 #[ test]
712782 fn compact_deletion_targets_removes_covered_paths ( ) {
713783 let dir = tempdir ( ) . unwrap ( ) ;
0 commit comments