defmodule JsonStreamEncoder do @moduledoc """ Primary API for encoding streaming JSON documents. """ @type io_stream :: IO.device() @opaque streamer :: %JsonStreamEncoder.CompactStreamer{} | %JsonStreamEncoder.IndentedStreamer{} @type key :: atom | binary | number | nonempty_charlist | boolean @type indentation :: boolean | binary @spec new(io_stream) :: streamer @doc """ Create a new streamer without indentation. """ def new(io_stream) do JsonStreamEncoder.CompactStreamer.new(io_stream) end @spec new(io_stream, indentation) :: streamer @doc """ Create a new streamer with the given indentation settings. Pass `false` for no indentation, `true` for default indentation (two spaces), or a `binary` representing the desired indentation string. """ def new(io_stream, false) do JsonStreamEncoder.CompactStreamer.new(io_stream) end def new(io_stream, true) do JsonStreamEncoder.IndentedStreamer.new(io_stream) end def new(io_stream, indent_value) do JsonStreamEncoder.IndentedStreamer.new(io_stream, indent_value) end @doc """ Encode an array into the stream, using the contents of the function to set elements. ## Examples ary(streamer, fn(stream) -> stream |> val(1) |> val("2") end) #=> [1,"2"] """ @spec ary(streamer, (streamer -> streamer)) :: streamer def ary(streamer, fn_ary) do fn_ary.(streamer |> ary_start) |> ary_end end @doc """ Start encoding an array. Always pair with `ary_end/1`. More often you will want to use `ary/2`. ## Examples streamer |> ary_start() |> val(1) |> val("2") |> ary_end() #=> [1,"2"] """ @spec ary_start(streamer) :: streamer def ary_start(streamer) do streamer.__struct__.ary_start(streamer) end @doc """ End an array you previously started encoding with `ary_start/1`. """ @spec ary_end(streamer) :: streamer def ary_end(streamer) do streamer.__struct__.ary_end(streamer) end @doc """ Encode an object into the stream, using the contents of the function to set key/value pairs. ## Examples obj(streamer, fn(stream) -> stream |> kv(1,"2") end) #=> {"1": "2"} """ @spec obj(streamer, (streamer -> streamer)) :: streamer def obj(streamer, fn_obj) do fn_obj.(streamer |> obj_start) |> obj_end end @doc """ Start encoding an object. Always pair with `obj_end/1`. More often you will want to use `obj/2`. ## Examples streamer |> obj_start() |> kv(1,"2") |> obj_end() #=> {"1": "2"} """ @spec obj_start(streamer) :: streamer def obj_start(streamer) do streamer.__struct__.obj_start(streamer) end @doc """ End an object you previously started encoding with `obj_start/1`. """ @spec obj_end(streamer) :: streamer def obj_end(streamer) do streamer.__struct__.obj_end(streamer) end @doc """ Encode a key for an object. Always follow this with a `val/2`, `obj/2`, or `ary/2` - otherwise your JSON won't make any sense. """ @spec key(streamer, key) :: streamer def key(streamer, k) do streamer.__struct__.key(streamer, k) end @doc """ Encode a value. Usable anywhere you would expect a JSON value: - The root of a document - After a key - As an array element """ @spec val(streamer, any) :: streamer def val(streamer, v) do streamer.__struct__.val(streamer, v) end @doc """ Encode a key-value pair for an object. You should really only use this if your value is simple, otherwise use `key/2` in comnbination with other functions. """ @spec kv(streamer, key, any) :: streamer def kv(streamer, k, v) do streamer |> key(k) |> val(v) end end