Current section
Files
Jump to
Current section
Files
lib/api_brasil/core/json.ex
defmodule ApiBrasil.Core.JSON do
@moduledoc """
Codec JSON da SDK — sem dependência obrigatória.
O codec é escolhido em tempo de execução, na primeira chamada, e fica
memorizado em `:persistent_term`:
1. [`Jason`](https://hex.pm/packages/jason), se estiver no projeto;
2. `JSON`, nativo do Elixir 1.18+;
3. `:json`, nativo do Erlang/OTP 27+.
Só é preciso adicionar o `:jason` ao projeto em versões anteriores a
Elixir 1.18 / OTP 27.
"""
@typedoc "Termo compatível com JSON: mapa, lista, string, número, booleano ou `nil`."
@type json :: map() | list() | String.t() | number() | boolean() | nil
@doc """
Codifica um termo em JSON.
Levanta se o termo não for serializável.
"""
@spec encode!(term()) :: binary()
def encode!(term) do
case codec() do
{:module, module} -> apply(module, :encode!, [term])
:otp -> IO.iodata_to_binary(:json.encode(term))
end
end
@doc """
Decodifica uma string JSON.
Objetos viram mapas com chaves binárias — o mesmo formato em todos os
codecs suportados.
"""
@spec decode(binary()) :: {:ok, json()} | {:error, term()}
def decode(binary) when is_binary(binary) do
case codec() do
{:module, module} ->
apply(module, :decode, [binary])
:otp ->
try do
{:ok, :json.decode(binary)}
rescue
error -> {:error, error}
catch
_kind, reason -> {:error, reason}
end
end
end
@doc """
Informa qual codec está em uso — `Jason`, `JSON` ou `:json`.
Útil para diagnosticar o ambiente.
"""
@spec codec_name() :: module() | :json
def codec_name do
case codec() do
{:module, module} -> module
:otp -> :json
end
end
defp codec do
case :persistent_term.get(__MODULE__, nil) do
nil ->
detected = detect()
:persistent_term.put(__MODULE__, detected)
detected
detected ->
detected
end
end
defp detect do
cond do
Code.ensure_loaded?(Jason) -> {:module, Jason}
Code.ensure_loaded?(JSON) -> {:module, JSON}
Code.ensure_loaded?(:json) -> :otp
true -> raise ArgumentError, no_codec_message()
end
end
defp no_codec_message do
"""
Nenhum codec JSON disponível.
Use Elixir 1.18+ (módulo JSON), Erlang/OTP 27+ (módulo :json) ou
adicione o Jason ao seu projeto:
{:jason, "~> 1.4"}
"""
end
end