package validate

  1. Overview
  2. Docs

Source file ppx_derive_validate.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
open Ppxlib
open Ast_helper
open Validators

let map_type_declaration ~loc td =
  let body =
    match td.ptype_kind with
    | Ptype_record label_declarations ->
        validate_record_exp ~loc label_declarations
    | Ptype_abstract ->
        td.ptype_manifest |> Option.get |> validate_abstract_exp ~loc
    | Ptype_variant constructor_declarations ->
        validate_variant_exp ~loc constructor_declarations
    | _ -> Location.raise_errorf ~loc "Unsupported type"
  in
  let type_name = td.ptype_name.txt in

  let param_pattern = Pat.var { txt = "x"; loc } in
  let param_type = Typ.constr { txt = Lident type_name; loc } [] in
  let typed_param_pattern = Pat.constraint_ param_pattern param_type in
  let func_expr = Exp.fun_ Nolabel None typed_param_pattern body in

  let function_name = "validate_" ^ type_name in

  let function_pattern = Pat.var { txt = function_name; loc } in

  Vb.mk function_pattern func_expr

let map_sig ~loc td =
  match td.ptype_kind with
  | Ptype_abstract | Ptype_record _ ->
      let record_name = td.ptype_name.txt in
      let function_name = "validate_" ^ record_name in
      let function_name_loc = { txt = function_name; loc } in

      let input_type = Typ.constr { txt = Lident record_name; loc } [] in
      let output_type =
        Typ.constr
          { txt = Ldot (Lident "Validate", "validation_error"); loc }
          []
      in
      let result_type =
        Typ.constr { txt = Lident "result"; loc } [ input_type; output_type ]
      in
      let function_type = Typ.arrow Nolabel input_type result_type in
      Sig.value (Val.mk function_name_loc function_type)
  | _ -> Location.raise_errorf ~loc "Unsupported type"

let is_recursive names td =
  match td.ptype_kind with
  | Ptype_record label_declarations ->
      names |> List.exists (lds_has_recursive label_declarations)
  | Ptype_abstract ->
      let ct = Option.get td.ptype_manifest in
      names |> List.exists (cts_has_recursive [ ct ])
  | Ptype_variant constructor_declarations ->
      let recursive cd =
        match cd.pcd_args with
        | Pcstr_tuple cts -> names |> List.exists (cts_has_recursive cts)
        | Pcstr_record lds -> names |> List.exists (lds_has_recursive lds)
      in
      constructor_declarations |> List.exists recursive
  | _ -> false

let generate_impl ~ctxt (_rec_flag, type_declarations) =
  let names = type_declarations |> List.map (fun td -> td.ptype_name.txt) in
  let is_recursive = type_declarations |> List.exists (is_recursive names) in
  let rec_flag = if is_recursive then Recursive else Nonrecursive in
  let loc = Expansion_context.Deriver.derived_item_loc ctxt in
  [
    type_declarations
    |> List.map (map_type_declaration ~loc)
    |> Str.value rec_flag;
  ]

let generate_intf ~ctxt (_rec_flag, type_declarations) =
  let loc = Expansion_context.Deriver.derived_item_loc ctxt in
  type_declarations |> List.map (map_sig ~loc)

let () =
  let impl_generator = Deriving.Generator.V2.make_noarg generate_impl in
  let intf_generator = Deriving.Generator.V2.make_noarg generate_intf in
  Deriving.add "validate" ~str_type_decl:impl_generator
    ~sig_type_decl:intf_generator
  |> Deriving.ignore
OCaml

Innovation. Community. Security.