package ppx_csv_conv

  1. Overview
  2. Docs
Generate functions to read/write records in csv format

Install

Dune Dependency

Authors

Maintainers

Sources

v0.17.0.tar.gz
sha256=069430f81559bfeca188da347f0e3aa7827bf69d8ac90e17d729c9d7e55fb4e5

doc/src/ppx_csv_conv_deprecated/ppx_csv_conv_deprecated.ml.html

Source file ppx_csv_conv_deprecated.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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
open Base
open Ppxlib
open Ast_builder.Default

let extension_name = "csv"

let unsupported_type_error_msg ~name =
  Printf.sprintf "The type %s is not natively supported in the csv camlp4 extension" name
;;

let useless_merge_recursive _log ~field_name:_ ~tp:_ ast = ast

let edot ~loc path_opt id =
  pexp_ident
    ~loc
    (Located.mk
       ~loc
       (match path_opt with
        | None -> Longident.Lident id
        | Some p -> Longident.Ldot (p, id)))
;;

(** Generate the list of fields contained in a flattened record type *)
module Rev_headers = Ppx_conv_func.Of_simple (struct
  let unsupported_type_error_msg = unsupported_type_error_msg
  let conversion_name = extension_name

  let function_name = function
    | None -> "rev_csv_header'"
    | Some param -> Printf.sprintf "rev_csv_header_of_%s'" param
  ;;

  let atoms loc ~field_name = [%expr fun acc _ -> [%e estring ~loc field_name] :: acc]
  let merge_recursive = useless_merge_recursive

  let recursive loc ~field_name ~type_name:_ ~path =
    let tns = function_name None in
    let recursive = edot ~loc path tns in
    let is_csv_atom = edot ~loc path "is_csv_atom" in
    [%expr
      fun acc _ ->
        if [%e is_csv_atom]
        then [%e estring ~loc field_name] :: acc
        else [%e recursive] acc () ()]
  ;;
end)

(* Generate the specification of the headers as a tree. This is useful to generate headers
   consisting of multiple rows, each field grouping those below. *)
module Spec_of_headers = Ppx_conv_func.Of_simple (struct
  let unsupported_type_error_msg = unsupported_type_error_msg
  let conversion_name = extension_name

  let function_name = function
    | None -> "rev_csv_header_spec'"
    | Some param -> Printf.sprintf "rev_csv_header_spec_of_%s'" param
  ;;

  let atoms loc ~field_name =
    [%expr fun acc _ -> Csvfields.Csv.Spec.Leaf [%e estring ~loc field_name] :: acc]
  ;;

  let merge_recursive = useless_merge_recursive

  let recursive loc ~field_name ~type_name:_ ~path =
    let tns = function_name None in
    let recursive = edot ~loc path tns in
    let is_csv_atom = edot ~loc path "is_csv_atom" in
    [%expr
      fun acc _ ->
        if [%e is_csv_atom]
        then Csvfields.Csv.Spec.Leaf [%e estring ~loc field_name] :: acc
        else
          Csvfields.Csv.Spec.Tree ([%e estring ~loc field_name], [%e recursive] [] () ())
          :: acc]
  ;;
end)

(** Generate the some type using a csv row (a list of strings) *)
module Type_of_csv_row = Ppx_conv_func.Of_complete (struct
  let unsupported_type_error_msg = unsupported_type_error_msg
  let conversion_name = extension_name

  let function_name = function
    | None -> failwith "Csv conversion of_row requires some name"
    | Some param -> Printf.sprintf "%s_of_row'" param
  ;;

  let unit loc ~field_name:_ = [%expr Csvfields.Csv.unit_of_row]
  let bool loc ~field_name:_ = [%expr Csvfields.Csv.bool_of_row]
  let string loc ~field_name:_ = [%expr Csvfields.Csv.string_of_row]
  let char loc ~field_name:_ = [%expr Csvfields.Csv.char_of_row]
  let int loc ~field_name:_ = [%expr Csvfields.Csv.int_of_row]
  let float loc ~field_name:_ = [%expr Csvfields.Csv.float_of_row]
  let int32 loc ~field_name:_ = [%expr Csvfields.Csv.int32_of_row]
  let int64 loc ~field_name:_ = [%expr Csvfields.Csv.int64_of_row]
  let nativeint loc ~field_name:_ = [%expr Csvfields.Csv.nativeint_of_row]
  let big_int loc ~field_name:_ = [%expr Csvfields.Csv.big_int_of_row]
  let nat loc ~field_name:_ = [%expr Csvfields.Csv.nat_of_row]
  let num loc ~field_name:_ = [%expr Csvfields.Csv.num_of_row]
  let ratio loc ~field_name:_ = [%expr Csvfields.Csv.ratio_of_row]
  let list loc ~field_name:_ = Ppx_conv_func.raise_unsupported ~loc "list"
  let array loc ~field_name:_ = Ppx_conv_func.raise_unsupported ~loc "list"
  let option loc ~field_name:_ = Ppx_conv_func.raise_unsupported ~loc "option"
  let lazy_t loc ~field_name:_ = Ppx_conv_func.raise_unsupported ~loc "lazy_t"
  let ref loc ~field_name:_ = Ppx_conv_func.raise_unsupported ~loc "ref"
  let merge_recursive = useless_merge_recursive

  let recursive loc ~field_name:_ ~type_name ~path =
    let tns = function_name (Some type_name) in
    edot ~loc path tns
  ;;
end)

module type B = sig
  val writer : Location.t -> arg_label * expression
  val is_first : Location.t -> arg_label * expression
  val is_last : Location.t -> arg_label * expression
end

module Make_row_of (S : B) = struct
  let unsupported_type_error_msg = unsupported_type_error_msg
  let conversion_name = extension_name

  let function_name = function
    | None -> failwith "Csv conversion write_row_of_ requires some name"
    | Some param -> Printf.sprintf "write_row_of_%s'" param
  ;;

  let add_arguments expr loc =
    pexp_apply ~loc expr [ S.is_first loc; S.is_last loc; S.writer loc ]
  ;;

  let unit loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_unit] loc
  let bool loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_bool] loc
  let string loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_string] loc
  let char loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_char] loc
  let int loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_int] loc
  let float loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_float] loc
  let int32 loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_int32] loc
  let int64 loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_int64] loc

  let nativeint loc ~field_name:_ =
    add_arguments [%expr Csvfields.Csv.row_of_nativeint] loc
  ;;

  let big_int loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_big_int] loc
  let nat loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_nat] loc
  let num loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_num] loc
  let ratio loc ~field_name:_ = add_arguments [%expr Csvfields.Csv.row_of_ratio] loc
  let merge_recursive = useless_merge_recursive

  let recursive loc ~field_name:_ ~type_name ~path =
    let tns = function_name (Some type_name) in
    add_arguments (edot ~loc path tns) loc
  ;;

  let list loc ~field_name:_ = Ppx_conv_func.raise_unsupported ~loc "list"
  let array loc ~field_name:_ = Ppx_conv_func.raise_unsupported ~loc "array"
  let option loc ~field_name:_ = Ppx_conv_func.raise_unsupported ~loc "option"
  let lazy_t loc ~field_name:_ = Ppx_conv_func.raise_unsupported ~loc "lazy_t"
  let ref loc ~field_name:_ = Ppx_conv_func.raise_unsupported ~loc "ref"
end

let falseexpr loc = [%expr false]

module Unique_row_of = Ppx_conv_func.Of_complete (Make_row_of (struct
  let writer loc = Labelled "writer", [%expr writer]
  let is_first loc = Labelled "is_first", [%expr is_first]
  let is_last loc = Labelled "is_last", [%expr is_last]
end))

module First_row_of = Ppx_conv_func.Of_complete (Make_row_of (struct
  let writer loc = Labelled "writer", [%expr writer]
  let is_first loc = Labelled "is_first", [%expr is_first]
  let is_last loc = Labelled "is_last", falseexpr loc
end))

module Middle_row_of = Ppx_conv_func.Of_complete (Make_row_of (struct
  let writer loc = Labelled "writer", [%expr writer]
  let is_first loc = Labelled "is_first", falseexpr loc
  let is_last loc = Labelled "is_last", falseexpr loc
end))

module Last_row_of = Ppx_conv_func.Of_complete (Make_row_of (struct
  let writer loc = Labelled "writer", [%expr writer]
  let is_first loc = Labelled "is_first", falseexpr loc
  let is_last loc = Labelled "is_last", [%expr is_last]
end))

let csv_record_sig loc ~record_name =
  let st =
    psig_include
      ~loc
      (include_infos
         ~loc
         (pmty_with
            ~loc
            (pmty_ident ~loc (Located.lident ~loc "Csvfields.Csv.Csvable"))
            [ Pwith_typesubst
                ( Located.lident ~loc "t"
                , type_declaration
                    ~loc
                    ~name:(Located.mk ~loc "t")
                    ~params:[]
                    ~manifest:
                      (Some (ptyp_constr ~loc (Located.lident ~loc record_name) []))
                    ~cstrs:[]
                    ~kind:Ptype_abstract
                    ~private_:Public )
            ]))
  in
  [ st ]
;;

let rev_csv_header' ~record_name ~lds loc =
  let name = [%pat? rev_csv_header'] in
  let conversion_of_type = Rev_headers.conversion_of_type in
  Ppx_conv_func.Gen_struct.generate_using_fold
    ~record_name
    ~pass_acc:true
    ~pass_anonymous:true
    ~conversion_of_type
    ~name
    ~lds
    loc
;;

let rev_csv_header_spec' ~record_name ~lds loc =
  let name = [%pat? rev_csv_header_spec'] in
  let conversion_of_type = Spec_of_headers.conversion_of_type in
  Ppx_conv_func.Gen_struct.generate_using_fold
    ~record_name
    ~pass_acc:true
    ~pass_anonymous:true
    ~conversion_of_type
    ~name
    ~lds
    loc
;;

let fields_module ~record_name ~loc ~suffix =
  Ast_helper.Exp.ident
    { loc
    ; txt =
        Longident.parse
          (Printf.sprintf
             "%s.%s"
             (match String.equal record_name "t" with
              | true -> "Fields"
              | false -> Printf.sprintf "Fields_of_%s" record_name)
             suffix)
    }
;;

let row_of_t' ~record_name ~lds loc =
  let init = [%expr [%e fields_module ~record_name ~loc ~suffix:"Direct.iter"] t] in
  let body =
    Ppx_conv_func.Gen_struct.make_body
      ~lds
      ~init
      loc
      ~unique_f:Unique_row_of.conversion_of_type
      ~first_f:First_row_of.conversion_of_type
      ~last_f:Last_row_of.conversion_of_type
      Middle_row_of.conversion_of_type
  in
  let anonymous = Ppx_conv_func.Gen_struct.anonymous loc in
  let func =
    [%expr fun ~is_first ~is_last ~writer [%p anonymous] [%p anonymous] t -> [%e body]]
  in
  [%stri let write_row_of_t' = [%e func]]
;;

let t_of_row' ~record_name ~lds loc =
  let init =
    [%expr [%e fields_module ~record_name ~loc ~suffix:"make_creator"] strings]
  in
  let body =
    let f = Type_of_csv_row.conversion_of_type in
    Ppx_conv_func.Gen_struct.make_body ~lds ~init loc f
  in
  let func =
    Ppx_conv_func.lambda
      loc
      [ Ppx_conv_func.Gen_struct.anonymous loc; [%pat? strings] ]
      body
  in
  [%stri let t_of_row' = [%e func]]
;;

let csv_record ~tps:_ ~record_name loc lds =
  let t_of_row' = t_of_row' ~record_name ~lds loc in
  let is_csv_atom = [%stri let is_csv_atom = false] in
  let row_of_t' = row_of_t' ~record_name ~lds loc in
  let rev_csv_header' = rev_csv_header' ~record_name ~lds loc in
  let rev_csv_header_spec' = rev_csv_header_spec' ~record_name ~lds loc in
  let t =
    if String.( <> ) record_name "t"
    then [%str type t = [%t ptyp_constr ~loc (Located.lident ~loc record_name) []]]
    else
      [%str
        type _t = t
        type t = _t]
  in
  let with_constraints =
    [ Pwith_typesubst
        ( Located.lident ~loc "t"
        , type_declaration
            ~loc
            ~name:(Located.mk ~loc "t")
            ~manifest:(Some (ptyp_constr ~loc (Located.lident ~loc record_name) []))
            ~kind:Ptype_abstract
            ~private_:Public
            ~params:[]
            ~cstrs:[] )
    ]
  in
  let applied_functor =
    pmod_apply
      ~loc
      (pmod_ident ~loc (Located.lident ~loc "Csvfields.Csv.Record"))
      (pmod_structure
         ~loc
         (t @ [ is_csv_atom; rev_csv_header'; rev_csv_header_spec'; t_of_row'; row_of_t' ]))
  in
  let st =
    pstr_include
      ~loc
      (include_infos
         ~loc
         (pmod_constraint
            ~loc
            applied_functor
            (pmty_with
               ~loc
               (pmty_ident ~loc (Located.lident ~loc "Csvfields.Csv.Csvable"))
               with_constraints)))
  in
  [ st
  ; [%stri let [%p pvar ~loc (record_name ^ "_of_row")] = t_of_row]
  ; [%stri let [%p pvar ~loc ("row_of_" ^ record_name)] = row_of_t]
  ; [%stri let [%p pvar ~loc (record_name ^ "_of_row'")] = t_of_row']
  ; [%stri let [%p pvar ~loc ("write_row_of_" ^ record_name ^ "'")] = write_row_of_t']
  ]
;;

let csv =
  let str_type_decl =
    Deriving.Generator.make
      Deriving.Args.empty
      (Ppx_conv_func.Gen_struct.generate ~extension_name ~record:csv_record)
      ~deps:[ Ppx_fields_conv.fields ]
  in
  let sig_type_decl =
    Deriving.Generator.make
      Deriving.Args.empty
      (Ppx_conv_func.Gen_sig.generate
         ~extension_name
         ~nil:(fun ~tps:_ ~record_name loc -> csv_record_sig loc ~record_name)
         ~record:(fun ~tps:_ ~record_name loc _ -> csv_record_sig loc ~record_name))
  in
  Deriving.add extension_name ~str_type_decl ~sig_type_decl
;;
OCaml

Innovation. Community. Security.