dependabot-hex 0.98.18 → 0.98.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/deps/jason/.fetch +0 -0
- data/helpers/deps/jason/.hex +2 -0
- data/helpers/deps/jason/CHANGELOG.md +60 -0
- data/helpers/deps/jason/LICENSE +13 -0
- data/helpers/deps/jason/README.md +179 -0
- data/helpers/deps/jason/hex_metadata.config +20 -0
- data/helpers/deps/jason/lib/codegen.ex +158 -0
- data/helpers/deps/jason/lib/decoder.ex +657 -0
- data/helpers/deps/jason/lib/encode.ex +630 -0
- data/helpers/deps/jason/lib/encoder.ex +216 -0
- data/helpers/deps/jason/lib/formatter.ex +253 -0
- data/helpers/deps/jason/lib/fragment.ex +11 -0
- data/helpers/deps/jason/lib/helpers.ex +90 -0
- data/helpers/deps/jason/lib/jason.ex +228 -0
- data/helpers/deps/jason/mix.exs +92 -0
- metadata +18 -3
@@ -0,0 +1,216 @@
|
|
1
|
+
defprotocol Jason.Encoder do
|
2
|
+
@moduledoc """
|
3
|
+
Protocol controlling how a value is encoded to JSON.
|
4
|
+
|
5
|
+
## Deriving
|
6
|
+
|
7
|
+
The protocol allows leveraging the Elixir's `@derive` feature
|
8
|
+
to simplify protocol implementation in trivial cases. Accepted
|
9
|
+
options are:
|
10
|
+
|
11
|
+
* `:only` - encodes only values of specified keys.
|
12
|
+
* `:except` - encodes all struct fields except specified keys.
|
13
|
+
|
14
|
+
By default all keys except the `:__struct__` key are encoded.
|
15
|
+
|
16
|
+
## Example
|
17
|
+
|
18
|
+
Let's assume a presence of the following struct:
|
19
|
+
|
20
|
+
defmodule Test do
|
21
|
+
defstruct [:foo, :bar, :baz]
|
22
|
+
end
|
23
|
+
|
24
|
+
If we were to call `@derive Jason.Encoder` just before `defstruct`,
|
25
|
+
an implementaion similar to the follwing implementation would be generated:
|
26
|
+
|
27
|
+
defimpl Jason.Encoder, for: Test do
|
28
|
+
def encode(value, opts) do
|
29
|
+
Jason.Encode.map(Map.take(value, [:foo, :bar, :baz]), opts)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
If we called `@derive {Jason.Encoder, only: [:foo]}`, an implementation
|
34
|
+
similar to the following implementation would be genrated:
|
35
|
+
|
36
|
+
defimpl Jason.Encoder, for: Test do
|
37
|
+
def encode(value, opts) do
|
38
|
+
Jason.Encode.map(Map.take(value, [:foo]), opts)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
If we called `@derive {Jason.Encoder, except: [:foo]}`, an implementation
|
43
|
+
similar to the following implementation would be generated:
|
44
|
+
|
45
|
+
defimpl Jason.Encoder, for: Test do
|
46
|
+
def encode(value, opts) do
|
47
|
+
Jason.Encode.map(Map.take(value, [:bar, :baz]), opts)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
The actually generated implementations are more efficient computing some data
|
52
|
+
during compilation similar to the macros from the `Jason.Helpers` module.
|
53
|
+
|
54
|
+
## Explicit implementation
|
55
|
+
|
56
|
+
If you wish to implement the protocol fully yourself, it is advised to
|
57
|
+
use functions from the `Jason.Encode` module to do the actual iodata
|
58
|
+
generation - they are highly optimized and verified to always produce
|
59
|
+
valid JSON.
|
60
|
+
"""
|
61
|
+
|
62
|
+
@type t :: term
|
63
|
+
@type opts :: Jason.Encode.opts()
|
64
|
+
|
65
|
+
@fallback_to_any true
|
66
|
+
|
67
|
+
@doc """
|
68
|
+
Encodes `value` to JSON.
|
69
|
+
|
70
|
+
The argument `opts` is opaque - it can be passed to various functions in
|
71
|
+
`Jason.Encode` (or to the protocol function itself) for encoding values to JSON.
|
72
|
+
"""
|
73
|
+
@spec encode(t, opts) :: iodata
|
74
|
+
def encode(value, opts)
|
75
|
+
end
|
76
|
+
|
77
|
+
defimpl Jason.Encoder, for: Any do
|
78
|
+
defmacro __deriving__(module, struct, opts) do
|
79
|
+
fields = fields_to_encode(struct, opts)
|
80
|
+
kv = Enum.map(fields, &{&1, generated_var(&1, __MODULE__)})
|
81
|
+
escape = quote(do: escape)
|
82
|
+
encode_map = quote(do: encode_map)
|
83
|
+
encode_args = [escape, encode_map]
|
84
|
+
kv_iodata = Jason.Codegen.build_kv_iodata(kv, encode_args)
|
85
|
+
|
86
|
+
quote do
|
87
|
+
defimpl Jason.Encoder, for: unquote(module) do
|
88
|
+
require Jason.Helpers
|
89
|
+
|
90
|
+
def encode(%{unquote_splicing(kv)}, {unquote(escape), unquote(encode_map)}) do
|
91
|
+
unquote(kv_iodata)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# The same as Macro.var/2 except it sets generated: true
|
98
|
+
defp generated_var(name, context) do
|
99
|
+
{name, [generated: true], context}
|
100
|
+
end
|
101
|
+
|
102
|
+
def encode(%_{} = struct, _opts) do
|
103
|
+
raise Protocol.UndefinedError,
|
104
|
+
protocol: @protocol,
|
105
|
+
value: struct,
|
106
|
+
description: """
|
107
|
+
Jason.Encoder protocol must always be explicitly implemented.
|
108
|
+
|
109
|
+
If you own the struct, you can derive the implementation specifying \
|
110
|
+
which fields should be encoded to JSON:
|
111
|
+
|
112
|
+
@derive {Jason.Encoder, only: [....]}
|
113
|
+
defstruct ...
|
114
|
+
|
115
|
+
It is also possible to encode all fields, although this should be \
|
116
|
+
used carefully to avoid accidentally leaking private information \
|
117
|
+
when new fields are added:
|
118
|
+
|
119
|
+
@derive Jason.Encoder
|
120
|
+
defstruct ...
|
121
|
+
|
122
|
+
Finally, if you don't own the struct you want to encode to JSON, \
|
123
|
+
you may use Protocol.derive/3 placed outside of any module:
|
124
|
+
|
125
|
+
Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...])
|
126
|
+
Protocol.derive(Jason.Encoder, NameOfTheStruct)
|
127
|
+
"""
|
128
|
+
end
|
129
|
+
|
130
|
+
def encode(value, _opts) do
|
131
|
+
raise Protocol.UndefinedError,
|
132
|
+
protocol: @protocol,
|
133
|
+
value: value,
|
134
|
+
description: "Jason.Encoder protocol must always be explicitly implemented"
|
135
|
+
end
|
136
|
+
|
137
|
+
defp fields_to_encode(struct, opts) do
|
138
|
+
cond do
|
139
|
+
only = Keyword.get(opts, :only) ->
|
140
|
+
only
|
141
|
+
|
142
|
+
except = Keyword.get(opts, :except) ->
|
143
|
+
Map.keys(struct) -- [:__struct__ | except]
|
144
|
+
|
145
|
+
true ->
|
146
|
+
Map.keys(struct) -- [:__struct__]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# The following implementations are formality - they are already covered
|
152
|
+
# by the main encoding mechanism in Jason.Encode, but exist mostly for
|
153
|
+
# documentation purposes and if anybody had the idea to call the protocol directly.
|
154
|
+
|
155
|
+
defimpl Jason.Encoder, for: Atom do
|
156
|
+
def encode(atom, opts) do
|
157
|
+
Jason.Encode.atom(atom, opts)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
defimpl Jason.Encoder, for: Integer do
|
162
|
+
def encode(integer, _opts) do
|
163
|
+
Jason.Encode.integer(integer)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
defimpl Jason.Encoder, for: Float do
|
168
|
+
def encode(float, _opts) do
|
169
|
+
Jason.Encode.float(float)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
defimpl Jason.Encoder, for: List do
|
174
|
+
def encode(list, opts) do
|
175
|
+
Jason.Encode.list(list, opts)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
defimpl Jason.Encoder, for: Map do
|
180
|
+
def encode(map, opts) do
|
181
|
+
Jason.Encode.map(map, opts)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
defimpl Jason.Encoder, for: BitString do
|
186
|
+
def encode(binary, opts) when is_binary(binary) do
|
187
|
+
Jason.Encode.string(binary, opts)
|
188
|
+
end
|
189
|
+
|
190
|
+
def encode(bitstring, _opts) do
|
191
|
+
raise Protocol.UndefinedError,
|
192
|
+
protocol: @protocol,
|
193
|
+
value: bitstring,
|
194
|
+
description: "cannot encode a bitstring to JSON"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
defimpl Jason.Encoder, for: [Date, Time, NaiveDateTime, DateTime] do
|
199
|
+
def encode(value, _opts) do
|
200
|
+
[?\", @for.to_iso8601(value), ?\"]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
defimpl Jason.Encoder, for: Decimal do
|
205
|
+
def encode(value, _opts) do
|
206
|
+
# silence the xref warning
|
207
|
+
decimal = Decimal
|
208
|
+
[?\", decimal.to_string(value), ?\"]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
defimpl Jason.Encoder, for: Jason.Fragment do
|
213
|
+
def encode(%{encode: encode}, opts) do
|
214
|
+
encode.(opts)
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,253 @@
|
|
1
|
+
defmodule Jason.Formatter do
|
2
|
+
@moduledoc ~S"""
|
3
|
+
Pretty-printing and minimizing functions for JSON-encoded data.
|
4
|
+
|
5
|
+
Input is required to be in an 8-bit-wide encoding such as UTF-8 or Latin-1
|
6
|
+
in `t:iodata/0` format. Input must ve valid JSON, invalid JSON may produce
|
7
|
+
unexpected results or errors.
|
8
|
+
"""
|
9
|
+
|
10
|
+
@type opts :: [
|
11
|
+
{:indent, iodata}
|
12
|
+
| {:line_separator, iodata}
|
13
|
+
| {:record_separator, iodata}
|
14
|
+
| {:after_colon, iodata}
|
15
|
+
]
|
16
|
+
|
17
|
+
import Record
|
18
|
+
defrecordp :opts, [:indent, :line, :record, :colon]
|
19
|
+
|
20
|
+
@doc ~S"""
|
21
|
+
Pretty-prints JSON-encoded `input`.
|
22
|
+
|
23
|
+
`input` may contain multiple JSON objects or arrays, optionally separated
|
24
|
+
by whitespace (e.g., one object per line). Objects in output will be
|
25
|
+
separated by newlines. No trailing newline is emitted.
|
26
|
+
|
27
|
+
## Options
|
28
|
+
|
29
|
+
* `:indent` - used for nested objects and arrays (default: two spaces - `" "`);
|
30
|
+
* `:line_separator` - used in nested objects (default: `"\n"`);
|
31
|
+
* `:record_separator` - separates root-level objects and arrays
|
32
|
+
(default is the value for `:line_separator` option);
|
33
|
+
* `:after_colon` - printed after a colon inside objects (default: one space - `" "`).
|
34
|
+
|
35
|
+
## Examples
|
36
|
+
|
37
|
+
iex> Jason.Formatter.pretty_print(~s|{"a":{"b": [1, 2]}}|)
|
38
|
+
~s|{
|
39
|
+
"a": {
|
40
|
+
"b": [
|
41
|
+
1,
|
42
|
+
2
|
43
|
+
]
|
44
|
+
}
|
45
|
+
}|
|
46
|
+
|
47
|
+
"""
|
48
|
+
@spec pretty_print(iodata, opts) :: binary
|
49
|
+
def pretty_print(input, opts \\ []) do
|
50
|
+
input
|
51
|
+
|> pretty_print_to_iodata(opts)
|
52
|
+
|> IO.iodata_to_binary()
|
53
|
+
end
|
54
|
+
|
55
|
+
@doc ~S"""
|
56
|
+
Pretty-prints JSON-encoded `input` and returns iodata.
|
57
|
+
|
58
|
+
This function should be preferred to `pretty_print/2`, if the pretty-printed
|
59
|
+
JSON will be handed over to one of the IO functions or sent
|
60
|
+
over the socket. The Erlang runtime is able to leverage vectorised
|
61
|
+
writes and avoid allocating a continuous buffer for the whole
|
62
|
+
resulting string, lowering memory use and increasing performance.
|
63
|
+
"""
|
64
|
+
@spec pretty_print_to_iodata(iodata, opts) :: iodata
|
65
|
+
def pretty_print_to_iodata(input, opts \\ []) do
|
66
|
+
opts = parse_opts(opts, " ", "\n", nil, " ")
|
67
|
+
|
68
|
+
depth = :first
|
69
|
+
empty = false
|
70
|
+
|
71
|
+
{output, _state} = pp_iodata(input, [], depth, empty, opts)
|
72
|
+
|
73
|
+
output
|
74
|
+
end
|
75
|
+
|
76
|
+
@doc ~S"""
|
77
|
+
Minimizes JSON-encoded `input`.
|
78
|
+
|
79
|
+
`input` may contain multiple JSON objects or arrays, optionally
|
80
|
+
separated by whitespace (e.g., one object per line). Minimized
|
81
|
+
output will contain one object per line. No trailing newline is emitted.
|
82
|
+
|
83
|
+
## Options
|
84
|
+
|
85
|
+
* `:record_separator` - controls the string used as newline (default: `"\n"`).
|
86
|
+
|
87
|
+
## Examples
|
88
|
+
|
89
|
+
iex> Jason.Formatter.minimize(~s|{ "a" : "b" , "c": \n\n 2}|)
|
90
|
+
~s|{"a":"b","c":2}|
|
91
|
+
|
92
|
+
"""
|
93
|
+
@spec minimize(iodata, opts) :: binary
|
94
|
+
def minimize(input, opts \\ []) do
|
95
|
+
input
|
96
|
+
|> minimize_to_iodata(opts)
|
97
|
+
|> IO.iodata_to_binary()
|
98
|
+
end
|
99
|
+
|
100
|
+
@doc ~S"""
|
101
|
+
Minimizes JSON-encoded `input` and returns iodata.
|
102
|
+
|
103
|
+
This function should be preferred to `minimize/2`, if the minimized
|
104
|
+
JSON will be handed over to one of the IO functions or sent
|
105
|
+
over the socket. The Erlang runtime is able to leverage vectorised
|
106
|
+
writes and avoid allocating a continuous buffer for the whole
|
107
|
+
resulting string, lowering memory use and increasing performance.
|
108
|
+
"""
|
109
|
+
@spec minimize_to_iodata(iodata, opts) :: iodata
|
110
|
+
def minimize_to_iodata(input, opts) do
|
111
|
+
record = Keyword.get(opts, :record_separator, "\n")
|
112
|
+
opts = opts(indent: "", line: "", record: record, colon: "")
|
113
|
+
|
114
|
+
depth = :first
|
115
|
+
empty = false
|
116
|
+
|
117
|
+
{output, _state} = pp_iodata(input, [], depth, empty, opts)
|
118
|
+
|
119
|
+
output
|
120
|
+
end
|
121
|
+
|
122
|
+
defp parse_opts([{option, value} | opts], indent, line, record, colon) do
|
123
|
+
value = IO.iodata_to_binary(value)
|
124
|
+
case option do
|
125
|
+
:indent -> parse_opts(opts, value, line, record, colon)
|
126
|
+
:record_separator -> parse_opts(opts, indent, line, value, colon)
|
127
|
+
:after_colon -> parse_opts(opts, indent, line, record, value)
|
128
|
+
:line_separator -> parse_opts(opts, indent, value, record || value, colon)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
defp parse_opts([], indent, line, record, colon) do
|
133
|
+
opts(indent: indent, line: line, record: record || line, colon: colon)
|
134
|
+
end
|
135
|
+
|
136
|
+
for depth <- 1..16 do
|
137
|
+
defp tab(" ", unquote(depth)), do: unquote(String.duplicate(" ", depth))
|
138
|
+
end
|
139
|
+
|
140
|
+
defp tab("", _), do: ""
|
141
|
+
defp tab(indent, depth), do: List.duplicate(indent, depth)
|
142
|
+
|
143
|
+
defp pp_iodata(<<>>, output_acc, depth, empty, opts) do
|
144
|
+
{output_acc, &pp_iodata(&1, &2, depth, empty, opts)}
|
145
|
+
end
|
146
|
+
|
147
|
+
defp pp_iodata(<<byte, rest::binary>>, output_acc, depth, empty, opts) do
|
148
|
+
pp_byte(byte, rest, output_acc, depth, empty, opts)
|
149
|
+
end
|
150
|
+
|
151
|
+
defp pp_iodata([], output_acc, depth, empty, opts) do
|
152
|
+
{output_acc, &pp_iodata(&1, &2, depth, empty, opts)}
|
153
|
+
end
|
154
|
+
|
155
|
+
defp pp_iodata([byte | rest], output_acc, depth, empty, opts) when is_integer(byte) do
|
156
|
+
pp_byte(byte, rest, output_acc, depth, empty, opts)
|
157
|
+
end
|
158
|
+
|
159
|
+
defp pp_iodata([head | tail], output_acc, depth, empty, opts) do
|
160
|
+
{output_acc, cont} = pp_iodata(head, output_acc, depth, empty, opts)
|
161
|
+
cont.(tail, output_acc)
|
162
|
+
end
|
163
|
+
|
164
|
+
defp pp_byte(byte, rest, output, depth, empty, opts) when byte in ' \n\r\t' do
|
165
|
+
pp_iodata(rest, output, depth, empty, opts)
|
166
|
+
end
|
167
|
+
|
168
|
+
defp pp_byte(byte, rest, output, depth, empty, opts) when byte in '{[' do
|
169
|
+
{out, depth} =
|
170
|
+
cond do
|
171
|
+
depth == :first -> {byte, 1}
|
172
|
+
depth == 0 -> {[opts(opts, :record), byte], 1}
|
173
|
+
empty -> {[opts(opts, :line), tab(opts(opts, :indent), depth), byte], depth + 1}
|
174
|
+
true -> {byte, depth + 1}
|
175
|
+
end
|
176
|
+
|
177
|
+
empty = true
|
178
|
+
pp_iodata(rest, [output, out], depth, empty, opts)
|
179
|
+
end
|
180
|
+
|
181
|
+
defp pp_byte(byte, rest, output, depth, true = _empty, opts) when byte in '}]' do
|
182
|
+
empty = false
|
183
|
+
depth = depth - 1
|
184
|
+
pp_iodata(rest, [output, byte], depth, empty, opts)
|
185
|
+
end
|
186
|
+
|
187
|
+
defp pp_byte(byte, rest, output, depth, false = empty, opts) when byte in '}]' do
|
188
|
+
depth = depth - 1
|
189
|
+
out = [opts(opts, :line), tab(opts(opts, :indent), depth), byte]
|
190
|
+
pp_iodata(rest, [output, out], depth, empty, opts)
|
191
|
+
end
|
192
|
+
|
193
|
+
defp pp_byte(byte, rest, output, depth, _empty, opts) when byte in ',' do
|
194
|
+
empty = false
|
195
|
+
out = [byte, opts(opts, :line), tab(opts(opts, :indent), depth)]
|
196
|
+
pp_iodata(rest, [output, out], depth, empty, opts)
|
197
|
+
end
|
198
|
+
|
199
|
+
defp pp_byte(byte, rest, output, depth, empty, opts) when byte in ':' do
|
200
|
+
out = [byte, opts(opts, :colon)]
|
201
|
+
pp_iodata(rest, [output, out], depth, empty, opts)
|
202
|
+
end
|
203
|
+
|
204
|
+
defp pp_byte(byte, rest, output, depth, empty, opts) do
|
205
|
+
out = if empty, do: [opts(opts, :line), tab(opts(opts, :indent), depth), byte], else: byte
|
206
|
+
empty = false
|
207
|
+
|
208
|
+
if byte == ?" do
|
209
|
+
pp_string(rest, [output, out], _in_bs = false, &pp_iodata(&1, &2, depth, empty, opts))
|
210
|
+
else
|
211
|
+
pp_iodata(rest, [output, out], depth, empty, opts)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
defp pp_string(<<>>, output_acc, in_bs, cont) do
|
216
|
+
{output_acc, &pp_string(&1, &2, in_bs, cont)}
|
217
|
+
end
|
218
|
+
|
219
|
+
defp pp_string(binary, output_acc, true = _in_bs, cont) when is_binary(binary) do
|
220
|
+
<<byte, rest::binary>> = binary
|
221
|
+
pp_string(rest, [output_acc, byte], false, cont)
|
222
|
+
end
|
223
|
+
|
224
|
+
defp pp_string(binary, output_acc, false = _in_bs, cont) when is_binary(binary) do
|
225
|
+
case :binary.match(binary, ["\"", "\\"]) do
|
226
|
+
:nomatch ->
|
227
|
+
{[output_acc | binary], &pp_string(&1, &2, false, cont)}
|
228
|
+
{pos, 1} ->
|
229
|
+
{head, tail} = :erlang.split_binary(binary, pos + 1)
|
230
|
+
case :binary.at(binary, pos) do
|
231
|
+
?\\ -> pp_string(tail, [output_acc | head], true, cont)
|
232
|
+
?" -> cont.(tail, [output_acc | head])
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
defp pp_string([], output_acc, in_bs, cont) do
|
238
|
+
{output_acc, &pp_string(&1, &2, in_bs, cont)}
|
239
|
+
end
|
240
|
+
|
241
|
+
defp pp_string([byte | rest], output_acc, in_bs, cont) when is_integer(byte) do
|
242
|
+
cond do
|
243
|
+
in_bs -> pp_string(rest, [output_acc, byte], false, cont)
|
244
|
+
byte == ?" -> cont.(rest, [output_acc, byte])
|
245
|
+
true -> pp_string(rest, [output_acc, byte], byte == ?\\, cont)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
defp pp_string([head | tail], output_acc, in_bs, cont) do
|
250
|
+
{output_acc, cont} = pp_string(head, output_acc, in_bs, cont)
|
251
|
+
cont.(tail, output_acc)
|
252
|
+
end
|
253
|
+
end
|