Source file csv_common.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
open Core
module Csv = Csvlib.Csv
type t =
{ : string list
; lines : string list list
}
[@@deriving compare, sexp]
let empty = { header = []; lines = [] }
let print_csv ?separator t = Csv.print ?separator (t.header :: t.lines)
let of_csvlib_csv = function
| [] -> failwith "missing csv header"
| :: lines -> { header; lines }
;;
let load ?separator file = of_csvlib_csv (Csv.load ?separator file)
let load_all ?separator files = List.map files ~f:(load ?separator)
module Or_file = struct
type nonrec t =
| Csv of t
| File of Filename.t
| Stdin
[@@deriving compare, sexp_of]
include struct
open Command.Param
let arg_type =
Arg_type.map Filename_unix.arg_type ~f:(fun filename ->
if String.( = ) filename "-" then Stdin else File filename)
;;
let default = Stdin
let anon = anon (maybe_with_default default ("filename" %: arg_type))
let flag =
flag
"-file"
(optional_with_default default arg_type)
~doc:"FILE read a files instead of stdin"
;;
end
let with_in_channel ?separator in_channel ~f =
protectx in_channel ~finally:In_channel.close ~f:(fun ic ->
f (of_csvlib_csv (Csv.load_in ?separator ic)))
;;
let with_all ?separator t ~f =
match t with
| Csv csv -> f csv
| File x -> with_in_channel ?separator (In_channel.create x) ~f
| Stdin -> with_in_channel ?separator In_channel.stdin ~f
;;
let with_in_channel_lines ?separator in_channel ~ ~f =
let print_csv_line line = Csv.print ?separator [ line ] in
let = ref None in
let wrap_f line =
match !header with
| None ->
let , state = header_f line in
header := Some (new_header, state);
print_csv_line new_header
| Some (, state) -> print_csv_line (f ~header ~state line)
in
protectx
in_channel
~f:(fun ic -> Csv.load_rows ?separator wrap_f ic)
~finally:In_channel.close
;;
let with_lines ?separator t ~ ~f =
match t with
| Csv { ; lines } ->
let , state = header_f header in
Csv.print ?separator (header :: List.map lines ~f:(f ~header ~state))
| Stdin | File "-" -> with_in_channel_lines ?separator In_channel.stdin ~header_f ~f
| File x -> with_in_channel_lines ?separator (In_channel.create x) ~header_f ~f
;;
end