diff --git a/lib_eio/fs.ml b/lib_eio/fs.ml index ed7ebc8e2..d4c27c27a 100644 --- a/lib_eio/fs.ml +++ b/lib_eio/fs.ml @@ -50,6 +50,15 @@ type 'a dir = ([> dir_ty] as 'a) r (** {2 Provider Interface} *) module Pi = struct + + module type PATH = sig + val split : path -> (path * string) option + (** The implementation of {!Path.split}. *) + + val join : path -> path -> path + (** The implementation of {!Path.(/)}. *) + end + module type DIR = sig type t @@ -76,6 +85,8 @@ module Pi = struct val chown : follow:bool -> ?uid:int64 -> ?gid:int64 -> t -> path -> unit val pp : t Fmt.t val native : t -> string -> string option + + include PATH end type (_, _, _) Resource.pi += diff --git a/lib_eio/path.ml b/lib_eio/path.ml index 267a3f7af..19e73f681 100644 --- a/lib_eio/path.ml +++ b/lib_eio/path.ml @@ -1,22 +1,9 @@ type 'a t = 'a Fs.dir * Fs.path -(* Like [Filename.is_relative] but always using "/" as the separator. *) -let is_relative = function - | "" -> true - | x -> x.[0] <> '/' - -(* Like [Filename.concat] but always using "/" as the separator. *) -let concat a b = - let l = String.length a in - if l = 0 || a.[l - 1] = '/' then a ^ b - else a ^ "/" ^ b - -let ( / ) (dir, p1) p2 = - match p1, p2 with - | p1, "" -> (dir, concat p1 p2) - | _, p2 when not (is_relative p2) -> (dir, p2) - | ".", p2 -> (dir, p2) - | p1, p2 -> (dir, concat p1 p2) +let ( / ) (d, p1) p2 = + let (Resource.T (_, ops)) = d in + let module X = (val (Resource.get ops Fs.Pi.Dir)) in + (d, X.join p1 p2) let pp f (Resource.T (t, ops), p) = let module X = (val (Resource.get ops Fs.Pi.Dir)) in @@ -32,37 +19,10 @@ let native_exn t = | Some p -> p | None -> raise (Fs.err (Not_native (Fmt.str "%a" pp t))) -(* Drop the first [n] characters from [s]. *) -let string_drop s n = - String.sub s n (String.length s - n) - -(* "/foo/bar//" -> "/foo/bar" - "///" -> "/" - "foo/bar" -> "foo/bar" - *) -let remove_trailing_slashes s = - let rec aux i = - if i <= 1 || s.[i - 1] <> '/' then ( - if i = String.length s then s - else String.sub s 0 i - ) else aux (i - 1) - in - aux (String.length s) - -let split (dir, p) = - match remove_trailing_slashes p with - | "" -> None - | "/" -> None - | p -> - match String.rindex_opt p '/' with - | None -> Some ((dir, ""), p) - | Some idx -> - let basename = string_drop p (idx + 1) in - let dirname = - if idx = 0 then "/" - else remove_trailing_slashes (String.sub p 0 idx) - in - Some ((dir, dirname), basename) +let split (d, p) = + let (Resource.T (_, ops)) = d in + let module X = (val (Resource.get ops Fs.Pi.Dir)) in + X.split p |> Option.map (fun (dirname, basename) -> ((d, dirname), basename)) let open_in ~sw t = let (Resource.T (dir, ops), path) = t in diff --git a/lib_eio/path.mli b/lib_eio/path.mli index 608a2e79b..425272c72 100644 --- a/lib_eio/path.mli +++ b/lib_eio/path.mli @@ -36,7 +36,10 @@ type 'a t = 'a Fs.dir * path val ( / ) : 'a t -> string -> 'a t (** [t / step] is [t] with [step] appended to [t]'s path, - or replacing [t]'s path if [step] is absolute: + or replacing [t]'s path if [step] stands alone + (for example, if it is absolute). + + For POSIX systems (e.g. Linux and macOS), it will behave like this: - [(fd, "foo") / "bar" = (fd, "foo/bar")] - [(fd, "foo") / "/bar" = (fd, "/bar")] *) @@ -69,7 +72,7 @@ val split : 'a t -> ('a t * string) option [split t = None] if there is nothing to split. - For example: + For POSIX systems (e.g. Linux and macOS), it will behave like this: - [split (root, "foo/bar") = Some ((root, "foo"), "bar")] - [split (root, "/foo/bar") = Some ((root, "/foo"), "bar")] diff --git a/lib_eio/utils/eio_utils.ml b/lib_eio/utils/eio_utils.ml index 8597e9fc5..500ba6394 100644 --- a/lib_eio/utils/eio_utils.ml +++ b/lib_eio/utils/eio_utils.ml @@ -6,3 +6,4 @@ module Lf_queue = Lf_queue module Suspended = Suspended module Zzz = Zzz module Dla = Dla +module Posix_path = Posix_path diff --git a/lib_eio/utils/posix_path.ml b/lib_eio/utils/posix_path.ml new file mode 100644 index 000000000..39ea07245 --- /dev/null +++ b/lib_eio/utils/posix_path.ml @@ -0,0 +1,49 @@ +(* Like [Filename.is_relative] but always using "/" as the separator. *) +let is_relative = function + | "" -> true + | x -> x.[0] <> '/' + +(* Like [Filename.concat] but always using "/" as the separator. *) +let concat a b = + let l = String.length a in + if l = 0 || a.[l - 1] = '/' then a ^ b + else a ^ "/" ^ b + +let join p1 p2 = + match p1, p2 with + | p1, "" -> concat p1 p2 + | _, p2 when not (is_relative p2) -> p2 + | ".", p2 -> p2 + | p1, p2 -> concat p1 p2 + +(* Drop the first [n] characters from [s]. *) +let string_drop s n = + String.sub s n (String.length s - n) + +(* "/foo/bar//" -> "/foo/bar" + "///" -> "/" + "foo/bar" -> "foo/bar" + *) +let remove_trailing_slashes s = + let rec aux i = + if i <= 1 || s.[i - 1] <> '/' then ( + if i = String.length s then s + else String.sub s 0 i + ) else aux (i - 1) + in + aux (String.length s) + +let split p = + match remove_trailing_slashes p with + | "" -> None + | "/" -> None + | p -> + match String.rindex_opt p '/' with + | None -> Some ("", p) + | Some idx -> + let basename = string_drop p (idx + 1) in + let dirname = + if idx = 0 then "/" + else remove_trailing_slashes (String.sub p 0 idx) + in + Some (dirname, basename) diff --git a/lib_eio/utils/posix_path.mli b/lib_eio/utils/posix_path.mli new file mode 100644 index 000000000..b44d39a44 --- /dev/null +++ b/lib_eio/utils/posix_path.mli @@ -0,0 +1,3 @@ +(** POSIX path syntax. *) + +include Eio.Fs.Pi.PATH diff --git a/lib_eio_linux/fs.ml b/lib_eio_linux/fs.ml index 37574e78d..831119354 100644 --- a/lib_eio_linux/fs.ml +++ b/lib_eio_linux/fs.ml @@ -169,6 +169,8 @@ end = struct let native t path = Some (native_internal t path) + + include Eio_utils.Posix_path end and Dir_handler : sig val v : (Dir.t, [`Dir | `Close]) Eio.Resource.handler diff --git a/lib_eio_posix/fs.ml b/lib_eio_posix/fs.ml index 6113800e7..4fcebc268 100644 --- a/lib_eio_posix/fs.ml +++ b/lib_eio_posix/fs.ml @@ -152,6 +152,8 @@ end = struct let native t path = Some (native_internal t path) + + include Eio_utils.Posix_path end and Handler : sig val v : (Dir.t, [`Dir | `Close]) Eio.Resource.handler diff --git a/lib_eio_windows/fs.ml b/lib_eio_windows/fs.ml index 3af5a967d..ed2b5a4e5 100755 --- a/lib_eio_windows/fs.ml +++ b/lib_eio_windows/fs.ml @@ -208,6 +208,8 @@ end = struct let native _t _path = failwith "TODO: Windows native" + + include Eio_utils.Posix_path (* todo: replace with Windows syntax *) end and Handler : sig val v : (Dir.t, [`Dir | `Close]) Eio.Resource.handler diff --git a/tests/fs.md b/tests/fs.md index 2b5b0d246..bd1d05ec7 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -273,8 +273,7 @@ Creating directories with nesting, symlinks, etc: # Split ```ocaml -let fake_dir : Eio.Fs.dir_ty r = Eio.Resource.T ((), Eio.Resource.handler []) -let split path = Eio.Path.split (fake_dir, path) |> Option.map (fun ((_, dirname), basename) -> dirname, basename) +let split = Eio_utils.Posix_path.split ``` ```ocaml