package ppx_yojson_conv

  1. Overview
  2. Docs

Source file attrs.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
open! Base
open! Ppxlib

let default =
  Attribute.declare
    "yojson.default"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr (pstr_eval __ nil ^:: nil))
    (fun x -> x)
;;

let drop_default =
  Attribute.declare
    "yojson.yojson_drop_default"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr (alt_option (pstr_eval __ nil ^:: nil) nil))
    (fun x -> x)
;;

let drop_default_equal =
  Attribute.declare
    "yojson.@yojson_drop_default.equal"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()
;;

let drop_default_compare =
  Attribute.declare
    "yojson.@yojson_drop_default.compare"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()
;;

let drop_default_yojson =
  Attribute.declare
    "yojson.@yojson_drop_default.yojson"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()
;;

let drop_if =
  Attribute.declare
    "yojson.yojson_drop_if"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr (pstr_eval __ nil ^:: nil))
    (fun x -> x)
;;

let opaque =
  Attribute.declare "yojson.opaque" Attribute.Context.core_type Ast_pattern.(pstr nil) ()
;;

let option =
  Attribute.declare
    "yojson.option"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()
;;

let allow_extra_fields_td =
  Attribute.declare
    "yojson.allow_extra_fields"
    Attribute.Context.type_declaration
    Ast_pattern.(pstr nil)
    ()
;;

let allow_extra_fields_cd =
  Attribute.declare
    "yojson.allow_extra_fields"
    Attribute.Context.constructor_declaration
    Ast_pattern.(pstr nil)
    ()
;;

let yojson_key =
  Attribute.declare
    "yojson.key"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr (pstr_eval (estring __) nil ^:: nil))
    (fun x -> x)
;;

let yojson_variant_name =
  Attribute.declare
    "yojson.name"
    Attribute.Context.constructor_declaration
    Ast_pattern.(pstr (pstr_eval (estring __) nil ^:: nil))
    (fun x -> x)
;;

let yojson_polymorphic_variant_name =
  Attribute.declare
    "yojson.name"
    Attribute.Context.rtag
    Ast_pattern.(pstr (pstr_eval (estring __) nil ^:: nil))
    (fun x -> x)
;;

let invalid_attribute ~loc attr description =
  Location.raise_errorf
    ~loc
    "ppx_yojson_conv: [@%s] is only allowed on type [%s]."
    (Attribute.name attr)
    description
;;

let fail_if_allow_extra_field_cd ~loc x =
  if Option.is_some (Attribute.get allow_extra_fields_cd x)
  then
    Location.raise_errorf
      ~loc
      "ppx_yojson_conv: [@@allow_extra_fields] is only allowed on inline records."
;;

let fail_if_allow_extra_field_td ~loc x =
  if Option.is_some (Attribute.get allow_extra_fields_td x)
  then (
    match x.ptype_kind with
    | Ptype_variant cds
      when List.exists cds ~f:(fun cd ->
             match cd.pcd_args with
             | Pcstr_record _ -> true
             | _ -> false) ->
      Location.raise_errorf
        ~loc
        "ppx_yojson_conv: [@@@@allow_extra_fields] only works on records. For inline \
         records, do: type t = A of { a : int } [@@allow_extra_fields] | B [@@@@deriving \
         yojson]"
    | _ ->
      Location.raise_errorf
        ~loc
        "ppx_yojson_conv: [@@@@allow_extra_fields] is only allowed on records.")
;;

module Record_field_handler = struct
  type common = [ `yojson_option of core_type ]

  let get_attribute attr ld ~f =
    Option.map (Attribute.get attr ld) ~f:(fun x -> f x, Attribute.name attr)
  ;;

  let create ~loc getters ld =
    let common_getters =
      [ (fun ld ->
          match Attribute.get option ld with
          | Some () ->
            (match ld.pld_type with
             | [%type: [%t? ty] option] -> Some (`yojson_option ty, "[@yojson.option]")
             | _ -> invalid_attribute ~loc option "_ option")
          | None -> None)
      ]
    in
    match List.filter_map (getters @ common_getters) ~f:(fun f -> f ld) with
    | [] -> None
    | [ (v, _) ] -> Some v
    | _ :: _ :: _ as attributes ->
      Location.raise_errorf
        ~loc
        "The following elements are mutually exclusive: %s"
        (String.concat ~sep:" " (List.map attributes ~f:snd))
  ;;

  module Of_yojson = struct
    type t =
      [ common
      | `default of expression
      ]

    let create ~loc ld =
      create ~loc [ get_attribute default ~f:(fun default -> `default default) ] ld
    ;;
  end

  module Yojson_of = struct
    type t =
      [ common
      | `drop_default of [ `no_arg | `compare | `equal | `yojson | `func of expression ]
      | `drop_if of expression
      | `keep
      ]

    let create ~loc ld =
      create
        ~loc
        [ get_attribute drop_default ~f:(function
            | None -> `drop_default `no_arg
            | Some e -> `drop_default (`func e))
        ; get_attribute drop_default_equal ~f:(fun () -> `drop_default `equal)
        ; get_attribute drop_default_compare ~f:(fun () -> `drop_default `compare)
        ; get_attribute drop_default_yojson ~f:(fun () -> `drop_default `yojson)
        ; get_attribute drop_if ~f:(fun x -> `drop_if x)
        ]
        ld
      |> Option.value ~default:`keep
    ;;
  end
end
OCaml

Innovation. Community. Security.