Skip to content

Commit 4db6533

Browse files
authored
Merge pull request #628 from talex5/rmtree
Add Path.rmtree ?missing_ok and allow non-directories
2 parents 4347a22 + 9158f45 commit 4db6533

File tree

3 files changed

+39
-14
lines changed

3 files changed

+39
-14
lines changed

lib_eio/path.ml

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,18 +165,30 @@ let rmdir t =
165165
let bt = Printexc.get_raw_backtrace () in
166166
Exn.reraise_with_context ex bt "removing directory %a" pp t
167167

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-
)
168+
let catch_missing ~missing_ok fn x =
169+
if missing_ok then
170+
try fn x
171+
with Exn.Io (Fs.E Not_found _, _) -> ()
172+
else fn x
173+
174+
let rec rmtree ~missing_ok t =
175+
match kind ~follow:false t with
176+
| `Directory ->
177+
Switch.run (fun sw ->
178+
match
179+
let t = open_dir ~sw t in
180+
t, read_dir t
181+
with
182+
| t, items -> List.iter (fun x -> rmtree ~missing_ok (t / x)) items
183+
| exception Exn.Io (Fs.E Not_found _, _) when missing_ok -> ()
176184
);
177-
rmdir t
185+
catch_missing ~missing_ok rmdir t
186+
| `Not_found when missing_ok -> ()
187+
| _ ->
188+
catch_missing ~missing_ok unlink t
178189

179-
let rmtree = (rmtree : Fs.dir_ty t -> unit :> [> Fs.dir_ty] t -> unit)
190+
let rmtree ?(missing_ok=false) t =
191+
rmtree ~missing_ok (t :> Fs.dir_ty t)
180192

181193
let rename t1 t2 =
182194
let (dir2, new_path) = t2 in

lib_eio/path.mli

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,13 @@ 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. *)
195+
val rmtree : ?missing_ok:bool -> _ t -> unit
196+
(** [rmtree t] removes [t] (and its contents, recursively, if it's a directory).
197+
198+
@param missing_ok If [false] (the default), raise an {!Fs.Not_found} IO error if [t] doesn't exist.
199+
If [true], ignore missing items.
200+
This applies recursively, allowing two processes
201+
to attempt to remove a tree at the same time. *)
197202

198203
val rename : _ t -> _ t -> unit
199204
(** [rename old_t new_t] atomically unlinks [old_t] and links it as [new_t].

tests/fs.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ 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
64+
let try_rmtree ?missing_ok path =
65+
match Path.rmtree ?missing_ok path with
6666
| () -> traceln "rmtree %a -> ok" Path.pp path
6767
| exception ex -> traceln "@[<h>%a@]" Eio.Exn.pp ex
6868
@@ -413,9 +413,17 @@ Removing something that doesn't exist or is out of scope:
413413
try_write_file ~create:(`Exclusive 0o600) (foo / "bar/file1") "data";
414414
try_rmtree foo;
415415
assert (Path.kind ~follow:false foo = `Not_found);
416+
traceln "A second rmtree is OK with missing_ok:";
417+
try_rmtree ~missing_ok:true foo;
418+
traceln "But not without:";
419+
try_rmtree ~missing_ok:false foo;
416420
+mkdirs <cwd:foo/bar/baz> -> ok
417421
+write <cwd:foo/bar/file1> -> ok
418422
+rmtree <cwd:foo> -> ok
423+
+A second rmtree is OK with missing_ok:
424+
+rmtree <cwd:foo> -> ok
425+
+But not without:
426+
+Eio.Io Fs Not_found _, removing file <cwd:foo>
419427
- : unit = ()
420428
```
421429

0 commit comments

Comments
 (0)