Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions lib_eio/fs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 +=
Expand Down
56 changes: 8 additions & 48 deletions lib_eio/path.ml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
7 changes: 5 additions & 2 deletions lib_eio/path.mli
Original file line number Diff line number Diff line change
Expand Up @@ -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")] *)
Expand Down Expand Up @@ -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")]
Expand Down
1 change: 1 addition & 0 deletions lib_eio/utils/eio_utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ module Lf_queue = Lf_queue
module Suspended = Suspended
module Zzz = Zzz
module Dla = Dla
module Posix_path = Posix_path
49 changes: 49 additions & 0 deletions lib_eio/utils/posix_path.ml
Original file line number Diff line number Diff line change
@@ -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)
3 changes: 3 additions & 0 deletions lib_eio/utils/posix_path.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(** POSIX path syntax. *)

include Eio.Fs.Pi.PATH
2 changes: 2 additions & 0 deletions lib_eio_linux/fs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions lib_eio_posix/fs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions lib_eio_windows/fs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions tests/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading