Skip to content

Commit f36cc2f

Browse files
committed
Improve docs for Jason.Formatter
1 parent d758e36 commit f36cc2f

File tree

2 files changed

+60
-63
lines changed

2 files changed

+60
-63
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,11 @@ Jason has a couple feature differences compared to Poison.
127127
* Jason follows the JSON spec more strictly, for example it does not allow
128128
unescaped newline characters in JSON strings - e.g. `"\"\n\""` will
129129
produce a decoding error.
130-
* no support for pretty printing.
131130
* no support for decoding into data structures (the `as:` option).
132131
* no built-in encoders for `MapSet`, `Range` and `Stream`.
133132
* no support for encoding arbitrary structs - explicit implementation
134133
of the `Jason.Encoder` protocol is always required.
134+
* different pretty-printing customisation options (default `pretty: true` works the same)
135135

136136
If you require encoders for any of the unsupported collection types, I suggest
137137
adding the needed implementations directly to your project:

lib/formatter.ex

Lines changed: 59 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
defmodule Jason.Formatter do
22
@moduledoc ~S"""
3-
`Jason.Formatter` provides pretty-printing and minimizing functions for
4-
JSON-encoded data.
3+
Pretty-printing and minimizing functions for JSON-encoded data.
54
6-
Input is required to be in an 8-bit-wide encoding such as UTF-8 or Latin-1,
7-
and is accepted in `iodata` (`binary` or `iolist`) format.
8-
9-
Output is provided in either `binary` or `iolist` format.
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.
108
"""
119

1210
@type opts :: [
@@ -20,25 +18,21 @@ defmodule Jason.Formatter do
2018
defrecordp :opts, [:indent, :line, :record, :colon]
2119

2220
@doc ~S"""
23-
Returns a binary containing a pretty-printed representation of
24-
JSON-encoded `iodata`.
21+
Pretty-prints JSON-encoded `input`.
2522
26-
`iodata` may contain multiple JSON objects or arrays, optionally separated
27-
by whitespace (e.g., one object per line). Objects in `pretty_print`ed
28-
output will be separated by newlines. No trailing newline is emitted.
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.
2926
30-
Options:
27+
## Options
3128
32-
* `:indent` sets the indentation string used for nested objects and
33-
arrays. The default indent setting is two spaces (`" "`).
34-
* `:line_separator` sets the newline string used in nested objects.
35-
The default setting is a line feed (`"\n"`).
36-
* `:record_separator` sets the string printed between root-level objects
37-
and arrays. The default setting is `opts[:line_separator]`.
38-
* `:after_colon` sets the string printed after a colon inside objects.
39-
The default setting is one space (`" "`).
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 - `" "`).
4034
41-
Example:
35+
## Examples
4236
4337
iex> Jason.Formatter.pretty_print(~s|{"a":{"b": [1, 2]}}|)
4438
~s|{
@@ -49,93 +43,96 @@ defmodule Jason.Formatter do
4943
]
5044
}
5145
}|
46+
5247
"""
5348
@spec pretty_print(iodata, opts) :: binary
54-
def pretty_print(iodata, opts \\ []) do
55-
iodata
49+
def pretty_print(input, opts \\ []) do
50+
input
5651
|> pretty_print_to_iodata(opts)
5752
|> IO.iodata_to_binary()
5853
end
5954

6055
@doc ~S"""
61-
Returns an iolist containing a pretty-printed representation of
62-
JSON-encoded `iodata`.
56+
Pretty-prints JSON-encoded `input` and returns iodata.
6357
64-
See `pretty_print/2` for details and options.
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.
6563
"""
6664
@spec pretty_print_to_iodata(iodata, opts) :: iodata
67-
def pretty_print_to_iodata(iodata, opts \\ []) do
68-
opts = parse_opts(opts, opts(indent: " ", line: "\n", record: nil, colon: " "))
69-
opts = opts(opts, record: opts(opts, :record) || opts(opts, :line))
65+
def pretty_print_to_iodata(input, opts \\ []) do
66+
opts = parse_opts(opts, " ", "\n", nil, " ")
7067

7168
depth = :first
7269
empty = false
7370

74-
{output, _state} = pp_iodata(iodata, [], depth, empty, opts)
71+
{output, _state} = pp_iodata(input, [], depth, empty, opts)
7572

7673
output
7774
end
7875

7976
@doc ~S"""
80-
Returns a binary containing a minimized representation of
81-
JSON-encoded `iodata`.
77+
Minimizes JSON-encoded `input`.
8278
83-
`iodata` may contain multiple JSON objects or arrays, optionally
84-
separated by whitespace (e.g., one object per line). `minimize`d
85-
output will contain one object per line. No trailing newline is emitted.
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.
8682
87-
The `:record_separator` option may be given to control the string
88-
used as newline (default `"\n"`). Other options are ignored.
83+
## Options
8984
90-
Example:
85+
* `:record_separator` - controls the string used as newline (default: `"\n"`).
86+
87+
## Examples
9188
9289
iex> Jason.Formatter.minimize(~s|{ "a" : "b" , "c": \n\n 2}|)
9390
~s|{"a":"b","c":2}|
91+
9492
"""
9593
@spec minimize(iodata, opts) :: binary
96-
def minimize(iodata, opts \\ []) do
97-
iodata
94+
def minimize(input, opts \\ []) do
95+
input
9896
|> minimize_to_iodata(opts)
9997
|> IO.iodata_to_binary()
10098
end
10199

102100
@doc ~S"""
103-
Returns an iolist containing a minimized representation of
104-
JSON-encoded `iodata`.
101+
Minimizes JSON-encoded `input` and returns iodata.
105102
106-
See `minimize/2` for details and options.
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.
107108
"""
108109
@spec minimize_to_iodata(iodata, opts) :: iodata
109-
def minimize_to_iodata(iodata, opts) do
110-
opts = parse_opts(opts, opts(indent: "", line: "", record: "\n", colon: ""))
110+
def minimize_to_iodata(input, opts) do
111+
record = Keyword.get(opts, :record_separator, "\n")
112+
opts = opts(indent: "", line: "", record: record, colon: "")
111113

112114
depth = :first
113115
empty = false
114116

115-
{output, _state} = pp_iodata(iodata, [], depth, empty, opts)
117+
{output, _state} = pp_iodata(input, [], depth, empty, opts)
116118

117119
output
118120
end
119121

120-
defp parse_opts(opts, defaults) do
121-
Enum.reduce(opts, defaults, fn
122-
{:indent, indent}, opts ->
123-
opts(opts, indent: IO.iodata_to_binary(indent))
124-
125-
{:line_separator, line}, opts ->
126-
line = IO.iodata_to_binary(line)
127-
opts(opts, line: line, record: opts(opts, :record) || line)
128-
129-
{:record_separator, record}, opts ->
130-
opts(opts, record: IO.iodata_to_binary(record))
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
131131

132-
{:after_colon, colon}, opts ->
133-
opts(opts, colon: IO.iodata_to_binary(colon))
134-
end)
132+
defp parse_opts([], indent, line, record, colon) do
133+
opts(indent: indent, line: line, record: record || line, colon: colon)
135134
end
136135

137-
@spec tab(String.t(), non_neg_integer) :: iodata()
138-
## Returns an iolist containing `depth` instances of `opts[:indent]`
139136
for depth <- 1..16 do
140137
defp tab(" ", unquote(depth)), do: unquote(String.duplicate(" ", depth))
141138
end

0 commit comments

Comments
 (0)