Skip to content

Commit 4347a22

Browse files
authored
Merge pull request #627 from talex5/rmtree
Add Path.rmtree
2 parents 166118b + e45a9a1 commit 4347a22

File tree

3 files changed

+43
-3
lines changed

3 files changed

+43
-3
lines changed

lib_eio/path.ml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ let open_out ~sw ?(append=false) ~create t =
7272
let open_dir ~sw t =
7373
let (Resource.T (dir, ops), path) = t in
7474
let module X = (val (Resource.get ops Fs.Pi.Dir)) in
75-
try X.open_dir dir ~sw path, ""
75+
try
76+
let sub = X.open_dir dir ~sw path, "" in
77+
(sub : [`Close | `Dir] t :> [< `Close | `Dir] t)
7678
with Exn.Io _ as ex ->
7779
let bt = Printexc.get_raw_backtrace () in
7880
Exn.reraise_with_context ex bt "opening directory %a" pp t
@@ -163,6 +165,19 @@ let rmdir t =
163165
let bt = Printexc.get_raw_backtrace () in
164166
Exn.reraise_with_context ex bt "removing directory %a" pp t
165167

168+
let rec rmtree (t : Fs.dir_ty t) =
169+
with_open_dir t (fun t ->
170+
read_dir t |> List.iter (fun name ->
171+
let item = t / name in
172+
match kind ~follow:false item with
173+
| `Directory -> rmtree item
174+
| _ -> unlink item
175+
)
176+
);
177+
rmdir t
178+
179+
let rmtree = (rmtree : Fs.dir_ty t -> unit :> [> Fs.dir_ty] t -> unit)
180+
166181
let rename t1 t2 =
167182
let (dir2, new_path) = t2 in
168183
let (Resource.T (dir, ops), old_path) = t1 in

lib_eio/path.mli

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,12 @@ val mkdirs : ?exists_ok:bool -> perm:File.Unix_perm.t -> _ t -> unit
139139
140140
@param exist_ok If [false] (the default) then we raise {! Fs.Already_exists} if [t] is already a directory. *)
141141

142-
val open_dir : sw:Switch.t -> _ t -> [`Close | dir_ty] t
142+
val open_dir : sw:Switch.t -> _ t -> [< `Close | dir_ty] t
143143
(** [open_dir ~sw t] opens [t].
144144
145145
This can be passed to functions to grant access only to the subtree [t]. *)
146146

147-
val with_open_dir : _ t -> ([`Close | dir_ty] t -> 'a) -> 'a
147+
val with_open_dir : _ t -> ([< `Close | dir_ty] t -> 'a) -> 'a
148148
(** [with_open_dir] is like [open_dir], but calls [fn dir] with the new directory and closes
149149
it automatically when [fn] returns (if it hasn't already been closed by then). *)
150150

@@ -192,6 +192,9 @@ val rmdir : _ t -> unit
192192
193193
Note: this usually requires the directory to be empty. *)
194194

195+
val rmtree : _ t -> unit
196+
(** [rmtree t] removes directory [t] and its contents, recursively. *)
197+
195198
val rename : _ t -> _ t -> unit
196199
(** [rename old_t new_t] atomically unlinks [old_t] and links it as [new_t].
197200

tests/fs.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ let try_rmdir path =
6161
| () -> traceln "rmdir %a -> ok" Path.pp path
6262
| exception ex -> traceln "@[<h>%a@]" Eio.Exn.pp ex
6363
64+
let try_rmtree path =
65+
match Path.rmtree path with
66+
| () -> traceln "rmtree %a -> ok" Path.pp path
67+
| exception ex -> traceln "@[<h>%a@]" Eio.Exn.pp ex
68+
6469
let chdir path =
6570
traceln "chdir %S" path;
6671
Unix.chdir path
@@ -397,6 +402,23 @@ Removing something that doesn't exist or is out of scope:
397402
- : unit = ()
398403
```
399404

405+
# Recursive removal
406+
407+
```ocaml
408+
# run @@ fun env ->
409+
Switch.run @@ fun sw ->
410+
let cwd = Eio.Stdenv.cwd env in
411+
let foo = cwd / "foo" in
412+
try_mkdirs (foo / "bar"/ "baz");
413+
try_write_file ~create:(`Exclusive 0o600) (foo / "bar/file1") "data";
414+
try_rmtree foo;
415+
assert (Path.kind ~follow:false foo = `Not_found);
416+
+mkdirs <cwd:foo/bar/baz> -> ok
417+
+write <cwd:foo/bar/file1> -> ok
418+
+rmtree <cwd:foo> -> ok
419+
- : unit = ()
420+
```
421+
400422
# Limiting to a subdirectory
401423

402424
Create a sandbox, write a file with it, then read it from outside:

0 commit comments

Comments
 (0)