@@ -464,12 +464,45 @@ fn resolveSetTimestamp(t: Io.File.SetTimestamp) ?i96 {
464464 };
465465}
466466
467- fn dirCreateDirPathImpl (_ : ? * anyopaque , _ : Io.Dir , _ : []const u8 , _ : Io.Dir.Permissions ) Io.Dir.CreateDirPathError ! Io.Dir.CreatePathStatus {
468- @panic ("TODO: dirCreateDirPath" );
467+ fn dirCreateDirPathImpl (_ : ? * anyopaque , dir : Io.Dir , sub_path : []const u8 , permissions : Io.Dir.Permissions ) Io.Dir.CreateDirPathError ! Io.Dir.CreatePathStatus {
468+ var it = Io .Dir .path .componentIterator (sub_path );
469+ var status : Io.Dir.CreatePathStatus = .existed ;
470+ var component = it .last () orelse return error .BadPathName ;
471+ while (true ) {
472+ var op = ev .DirCreateDir .init (stdIoHandleToZio (dir .handle ), component .path , permissionsToZioMode (permissions ));
473+ try waitForIo (& op .c );
474+ if (op .getResult ()) | _ | {
475+ status = .created ;
476+ } else | err | switch (err ) {
477+ error .PathAlreadyExists = > {
478+ const kind = try filePathKind (dir , component .path );
479+ if (kind != .directory ) return error .NotDir ;
480+ },
481+ error .FileNotFound = > {
482+ component = it .previous () orelse return error .FileNotFound ;
483+ continue ;
484+ },
485+ else = > | e | return e ,
486+ }
487+ component = it .next () orelse return status ;
488+ }
469489}
470490
471- fn dirCreateDirPathOpenImpl (_ : ? * anyopaque , _ : Io.Dir , _ : []const u8 , _ : Io.Dir.Permissions , _ : Io.Dir.OpenOptions ) Io.Dir.CreateDirPathOpenError ! Io.Dir {
472- @panic ("TODO: dirCreateDirPathOpen" );
491+ fn filePathKind (dir : Io.Dir , sub_path : []const u8 ) Io.Dir.StatFileError ! Io.File.Kind {
492+ var op = ev .FileStat .init (stdIoHandleToZio (dir .handle ), sub_path , .{ .follow_symlinks = false });
493+ try waitForIo (& op .c );
494+ const info = op .getResult () catch | err | return statFileErrToStdErr (err );
495+ return zioKindToStdIoKind (info .kind );
496+ }
497+
498+ fn dirCreateDirPathOpenImpl (_ : ? * anyopaque , dir : Io.Dir , sub_path : []const u8 , permissions : Io.Dir.Permissions , options : Io.Dir.OpenOptions ) Io.Dir.CreateDirPathOpenError ! Io.Dir {
499+ return dirOpenDirImpl (null , dir , sub_path , options ) catch | err | switch (err ) {
500+ error .FileNotFound = > {
501+ _ = try dirCreateDirPathImpl (null , dir , sub_path , permissions );
502+ return dirOpenDirImpl (null , dir , sub_path , options );
503+ },
504+ else = > | e | return e ,
505+ };
473506}
474507
475508fn dirOpenDirImpl (_ : ? * anyopaque , dir : Io.Dir , sub_path : []const u8 , options : Io.Dir.OpenOptions ) Io.Dir.OpenError ! Io.Dir {
@@ -2656,6 +2689,72 @@ test "io: dir create/delete" {
26562689 try std .testing .expectError (error .FileNotFound , dir .deleteDir (io , dir_path ));
26572690}
26582691
2692+ test "io: dir createDirPath creates nested directories" {
2693+ const rt = try Runtime .init (std .testing .allocator , .{});
2694+ defer rt .deinit ();
2695+ const io = rt .io ();
2696+
2697+ const dir : Io.Dir = .cwd ();
2698+ const sep = Io .Dir .path .sep_str ;
2699+ const nested_path = "test_io_createDirPath" ++ sep ++ "a" ++ sep ++ "b" ++ sep ++ "c" ;
2700+ const base_path = "test_io_createDirPath" ;
2701+
2702+ // Clean up from previous failed runs.
2703+ dir .deleteDir (io , "test_io_createDirPath" ++ sep ++ "a" ++ sep ++ "b" ++ sep ++ "c" ) catch {};
2704+ dir .deleteDir (io , "test_io_createDirPath" ++ sep ++ "a" ++ sep ++ "b" ) catch {};
2705+ dir .deleteDir (io , "test_io_createDirPath" ++ sep ++ "a" ) catch {};
2706+ dir .deleteDir (io , base_path ) catch {};
2707+
2708+ defer {
2709+ dir .deleteDir (io , "test_io_createDirPath" ++ sep ++ "a" ++ sep ++ "b" ++ sep ++ "c" ) catch {};
2710+ dir .deleteDir (io , "test_io_createDirPath" ++ sep ++ "a" ++ sep ++ "b" ) catch {};
2711+ dir .deleteDir (io , "test_io_createDirPath" ++ sep ++ "a" ) catch {};
2712+ dir .deleteDir (io , base_path ) catch {};
2713+ }
2714+
2715+ // Create nested path from scratch.
2716+ const status = try dir .createDirPathStatus (io , nested_path , .default_dir );
2717+ try std .testing .expectEqual (Io .Dir .CreatePathStatus .created , status );
2718+
2719+ // Verify it exists.
2720+ var sub = try dir .openDir (io , nested_path , .{});
2721+ sub .close (io );
2722+
2723+ // Creating again should return .existed.
2724+ const status2 = try dir .createDirPathStatus (io , nested_path , .default_dir );
2725+ try std .testing .expectEqual (Io .Dir .CreatePathStatus .existed , status2 );
2726+ }
2727+
2728+ test "io: dir createDirPathOpen creates and opens" {
2729+ const rt = try Runtime .init (std .testing .allocator , .{});
2730+ defer rt .deinit ();
2731+ const io = rt .io ();
2732+
2733+ const dir : Io.Dir = .cwd ();
2734+ const sep = Io .Dir .path .sep_str ;
2735+ const nested_path = "test_io_createDirPathOpen" ++ sep ++ "x" ++ sep ++ "y" ;
2736+ const base_path = "test_io_createDirPathOpen" ;
2737+
2738+ // Clean up from previous failed runs.
2739+ dir .deleteDir (io , "test_io_createDirPathOpen" ++ sep ++ "x" ++ sep ++ "y" ) catch {};
2740+ dir .deleteDir (io , "test_io_createDirPathOpen" ++ sep ++ "x" ) catch {};
2741+ dir .deleteDir (io , base_path ) catch {};
2742+
2743+ defer {
2744+ dir .deleteDir (io , "test_io_createDirPathOpen" ++ sep ++ "x" ++ sep ++ "y" ) catch {};
2745+ dir .deleteDir (io , "test_io_createDirPathOpen" ++ sep ++ "x" ) catch {};
2746+ dir .deleteDir (io , base_path ) catch {};
2747+ }
2748+
2749+ // Create and open nested path.
2750+ var sub = try dir .createDirPathOpen (io , nested_path , .{});
2751+ sub .close (io );
2752+
2753+ // Should be able to open it again.
2754+ var sub2 = try dir .openDir (io , nested_path , .{});
2755+ sub2 .close (io );
2756+ }
2757+
26592758test "io: dir iterate over files" {
26602759 // NetBSD's getdirentries can return dirents with either 32-bit or
26612760 // 64-bit d_fileno depending on the filesystem, shifting all field
0 commit comments