Source file path.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
type fragment = string
type t = Relative of fragment list | Absolute of fragment list
let rel x = Relative x
let abs x = Absolute x
let root = abs []
let pwd = rel []
let equal_fragment = String.equal
let pp_fragment ppf = Format.fprintf ppf "%s"
let get_fragments = function Relative f | Absolute f -> f
let get_ctor_and_fragments = function
| Relative f -> (rel, f)
| Absolute f -> (abs, f)
let equal a b =
match (a, b) with
| Relative a, Relative b | Absolute a, Absolute b ->
List.equal equal_fragment a b
| _ -> false
let pp ppf path =
let prefix, fragments, start =
match path with
| Relative f -> (Filename.current_dir_name, f, 1)
| Absolute f -> ("", f, 0)
in
let seperator = Filename.dir_sep in
match fragments with
| [] -> Format.fprintf ppf "%s" (prefix ^ seperator)
| _ ->
let sep_len = String.length seperator in
let len =
List.fold_left
(fun acc fragment -> sep_len + acc + String.length fragment)
start fragments
in
let buf = Buffer.create len in
let () = Buffer.add_string buf prefix in
let () =
List.iter
(fun fragment ->
let () = Buffer.add_string buf seperator in
Buffer.add_string buf fragment)
fragments
in
Format.fprintf ppf "%s" (Buffer.contents buf)
let to_string = Format.asprintf "%a" pp
let to_list = function Absolute xs -> "/" :: xs | Relative xs -> "." :: xs
let to_pair = function Absolute xs -> (`Root, xs) | Relative xs -> (`Rel, xs)
let from_string str =
match String.split_on_char '/' str with
| "" :: "" :: xs | "" :: xs -> abs xs
| "." :: "" :: xs | "." :: xs -> rel xs
| xs -> rel xs
let append path fragments =
match path with
| Relative f -> Relative (f @ fragments)
| Absolute f -> Absolute (f @ fragments)
let extension path =
let fragments = get_fragments path in
let rec aux = function
| [] -> ""
| [ x ] -> Filename.extension x
| _ :: xs -> aux xs
in
aux fragments
let extension_opt path =
match extension path with "" -> None | ext -> Some ext
let make_extension extension =
let len = String.length extension in
if len = 0 then ""
else if len = 1 && String.equal extension "." then ""
else if len > 1 && extension.[0] = '.' then extension
else "." ^ extension
let has_extension ext path =
let ext = make_extension ext in
let fex = extension path in
String.equal ext fex
let update_last_fragment callback path =
let f, fragments = get_ctor_and_fragments path in
let rec aux acc = function
| [] -> path
| [ x ] -> f @@ List.rev (callback x :: acc)
| x :: xs -> aux (x :: acc) xs
in
aux [] fragments
let remove_extension = update_last_fragment Filename.remove_extension
let fragment_add_extension ext fragment = fragment ^ make_extension ext
let add_extension extension =
update_last_fragment (fragment_add_extension extension)
let change_extension extension =
update_last_fragment (fun fragment ->
fragment |> Filename.remove_extension |> fragment_add_extension extension)
let basename path =
let fragments = get_fragments path in
let rec aux = function [] -> None | [ x ] -> Some x | _ :: xs -> aux xs in
aux fragments
let dirname path =
let ctor, fragments = get_ctor_and_fragments path in
let rec aux acc = function
| [] -> []
| [ _ ] -> List.rev acc
| x :: xs -> aux (x :: acc) xs
in
ctor (aux [] fragments)
let move ~into source =
match basename source with None -> into | Some x -> append into [ x ]
let remove_common_prefix into source =
let rec aux acc into source =
match (into, source) with
| [ x ], y :: xs when String.equal x y -> List.rev_append acc (x :: xs)
| x :: xs, y :: ys when String.equal x y -> aux (x :: acc) xs ys
| _ -> into @ source
in
aux [] into source
let relocate ~into source =
match (into, source) with
| Relative x, Absolute y -> Relative (x @ y)
| Absolute x, Relative y -> Absolute (x @ y)
| Relative x, Relative y -> Relative (remove_common_prefix x y)
| Absolute x, Absolute y -> Absolute (remove_common_prefix x y)
let compare a b =
match (a, b) with
| Absolute _, Relative _ -> -1
| Relative _, Absolute _ -> 1
| Absolute a, Absolute b | Relative a, Relative b ->
List.compare String.compare a b
let to_sexp path =
let ctor, fragments =
match path with Relative x -> ("rel", x) | Absolute x -> ("abs", x)
in
Sexp.(node [ atom ctor; node @@ List.map atom fragments ])
let all_are_nodes sexp node =
List.fold_left
(fun acc value ->
Result.bind acc (fun acc ->
match value with
| Sexp.Atom x -> Ok (x :: acc)
| _ -> Error (Sexp.Invalid_sexp (sexp, "path"))))
(Ok []) node
|> Result.map List.rev
let from_sexp sexp =
match sexp with
| Sexp.(Node [ Atom "rel"; Node fragments ]) ->
Result.map rel (all_are_nodes sexp fragments)
| Sexp.(Node [ Atom "abs"; Node fragments ]) ->
Result.map abs (all_are_nodes sexp fragments)
| _ -> Error (Sexp.Invalid_sexp (sexp, "path"))
module Infix = struct
let ( ++ ) = append
let ( / ) path fragment = append path [ fragment ]
let ( ~/ ) = rel
end
include Infix
module Map = Map.Make (struct
type nonrec t = t
let compare = compare
end)