diff --git a/core/CCIO.ml b/core/CCIO.ml index 1e9313d1..b0c27718 100644 --- a/core/CCIO.ml +++ b/core/CCIO.ml @@ -449,6 +449,69 @@ module Seq = struct >>= fun () -> flush oc end +(** {6 File and file names} *) + +module File = struct + type t = string + + let to_string f = f + + let make f = + if Filename.is_relative f + then Filename.concat (Sys.getcwd()) f + else f + + let exists f = Wrap (fun () -> Sys.file_exists f) + + let is_directory f = Wrap (fun () -> Sys.is_directory f) + + let remove f = Wrap (fun () -> Sys.remove f) + + let _read_dir d () = + if Sys.is_directory d + then + let arr = Sys.readdir d in + Seq.of_array arr + |> Seq.map_pure make + else Seq.empty + + let rec _walk d () = + if Sys.is_directory d + then + let arr = Sys.readdir d in + let tail = Seq.of_array arr + |> Seq.flat_map + (fun s -> return (_walk (Filename.concat d s) ())) + in Seq.cons (`Dir,d) tail + else Seq.singleton (`File, d) + + let walk t = Wrap (_walk t) + + let read_dir ?(recurse=false) d = + if recurse + then walk d + >|= Seq.filter_map + (function + | `File, f -> Some f + | `Dir, _ -> None + ) + else Wrap (_read_dir d) + + let rec _read_dir_rec d () = + if Sys.is_directory d + then + let arr = Sys.readdir d in + Seq.of_array arr + |> Seq.map_pure (fun s -> Filename.concat d s) + |> Seq.flat_map + (fun s -> + if Sys.is_directory s + then return (_read_dir_rec s ()) + else return (Seq.singleton s) + ) + else Seq.empty +end + (** {2 Raw} *) module Raw = struct diff --git a/core/CCIO.mli b/core/CCIO.mli index 58e24dea..9fc78d96 100644 --- a/core/CCIO.mli +++ b/core/CCIO.mli @@ -270,6 +270,48 @@ module Seq : sig It blocks until all values of [seq] are produced and written to [oc]. *) end +(** {6 File and file names} + +How to list recursively files in a directory: +{[ + CCIO.( + File.read_dir ~recurse:true (File.make "/tmp") + >>= Seq.output ~sep:"\n" stdout + ) |> CCIO.run_exn ;; + + ]} + +See {!File.walk} if you also need to list directories. +*) + +module File : sig + type t = string + (** A file is always represented by its absolute path *) + + val to_string : t -> string + + val make : string -> t + (** Build a file representation from a path (absolute or relative) *) + + val exists : t -> bool io + + val is_directory : t -> bool io + + val remove : t -> unit io + + val read_dir : ?recurse:bool -> t -> t Seq.t io + (** [read_dir d] returns a sequence of files and directory contained + in the directory [d] (or an empty stream if [d] is not a directory) + @param recurse if true (default [false]), sub-directories are also + explored *) + + val walk : t -> ([`File | `Dir] * t) Seq.t io + (** similar to {!read_dir} (with [recurse=true]), this function walks + a directory recursively and yields either files or directories. + Is a file anything that doesn't satisfy {!is_directory} (including + symlinks, etc.) *) +end + (** {2 Low level access} *) module Raw : sig val wrap : (unit -> 'a) -> 'a t