defmodule Jason.Sigil do
@doc ~S"""
Handles the sigil `~j` for JSON strings.
Calls `Jason.decode!/2` with modifiers mapped to options.
Given a string literal without interpolations, decodes the
string at compile-time.
## Modifiers
See `Jason.decode/2` for detailed descriptions.
* `a` - equivalent to `{:keys, :atoms}` option
* `A` - equivalent to `{:keys, :atoms!}` option
* `r` - equivalent to `{:strings, :reference}` option
* `c` - equivalent to `{:strings, :copy}` option
## Examples
iex> ~j"0"
0
iex> ~j"[1, 2, 3]"
[1, 2, 3]
iex> ~j'"string"'r
"string"
iex> ~j"{}"
%{}
iex> ~j'{"atom": "value"}'a
%{atom: "value"}
iex> ~j'{"#{:j}": #{~c'"j"'}}'A
%{j: "j"}
"""
defmacro sigil_j(term, modifiers)
defmacro sigil_j({:<<>>, _meta, [string]}, modifiers) when is_binary(string) do
Macro.escape(Jason.decode!(string, mods_to_opts(modifiers)))
end
defmacro sigil_j(term, modifiers) do
quote(do: Jason.decode!(unquote(term), unquote(mods_to_opts(modifiers))))
end
@doc ~S"""
Handles the sigil `~J` for raw JSON strings.
Decodes a raw string ignoring Elixir interpolations and
escape characters at compile-time.
## Examples
iex> ~J'"#{string}"'
"\#{string}"
iex> ~J'"\u0078\\y"'
"x\\y"
iex> ~J'{"#{key}": "#{}"}'a
%{"\#{key}": "\#{}"}
"""
defmacro sigil_J(term, modifiers)
defmacro sigil_J({:<<>>, _meta, [string]}, modifiers) when is_binary(string) do
Macro.escape(Jason.decode!(string, mods_to_opts(modifiers)))
end
@spec mods_to_opts(charlist) :: [Jason.decode_opt()]
defp mods_to_opts(modifiers) do
modifiers
|> Enum.map(fn
?a -> {:keys, :atoms}
?A -> {:keys, :atoms!}
?r -> {:strings, :reference}
?c -> {:strings, :copy}
m -> raise ArgumentError, "unknown sigil modifier #{<<?", m, ?">>}"
end)
end
end