sigil.ex (1953B)
1 defmodule Jason.Sigil do 2 @doc ~S""" 3 Handles the sigil `~j` for JSON strings. 4 5 Calls `Jason.decode!/2` with modifiers mapped to options. 6 7 Given a string literal without interpolations, decodes the 8 string at compile-time. 9 10 ## Modifiers 11 12 See `Jason.decode/2` for detailed descriptions. 13 14 * `a` - equivalent to `{:keys, :atoms}` option 15 * `A` - equivalent to `{:keys, :atoms!}` option 16 * `r` - equivalent to `{:strings, :reference}` option 17 * `c` - equivalent to `{:strings, :copy}` option 18 19 ## Examples 20 21 iex> ~j"0" 22 0 23 24 iex> ~j"[1, 2, 3]" 25 [1, 2, 3] 26 27 iex> ~j'"string"'r 28 "string" 29 30 iex> ~j"{}" 31 %{} 32 33 iex> ~j'{"atom": "value"}'a 34 %{atom: "value"} 35 36 iex> ~j'{"#{:j}": #{'"j"'}}'A 37 %{j: "j"} 38 39 """ 40 defmacro sigil_j(term, modifiers) 41 42 defmacro sigil_j({:<<>>, _meta, [string]}, modifiers) when is_binary(string) do 43 Macro.escape(Jason.decode!(string, mods_to_opts(modifiers))) 44 end 45 46 defmacro sigil_j(term, modifiers) do 47 quote(do: Jason.decode!(unquote(term), unquote(mods_to_opts(modifiers)))) 48 end 49 50 @doc ~S""" 51 Handles the sigil `~J` for raw JSON strings. 52 53 Decodes a raw string ignoring Elixir interpolations and 54 escape characters at compile-time. 55 56 ## Examples 57 58 iex> ~J'"#{string}"' 59 "\#{string}" 60 61 iex> ~J'"\u0078\\y"' 62 "x\\y" 63 64 iex> ~J'{"#{key}": "#{}"}'a 65 %{"\#{key}": "\#{}"} 66 """ 67 defmacro sigil_J(term, modifiers) 68 69 defmacro sigil_J({:<<>>, _meta, [string]}, modifiers) when is_binary(string) do 70 Macro.escape(Jason.decode!(string, mods_to_opts(modifiers))) 71 end 72 73 @spec mods_to_opts(charlist) :: [Jason.decode_opt()] 74 defp mods_to_opts(modifiers) do 75 modifiers 76 |> Enum.map(fn 77 ?a -> {:keys, :atoms} 78 ?A -> {:keys, :atoms!} 79 ?r -> {:strings, :reference} 80 ?c -> {:strings, :copy} 81 m -> raise ArgumentError, "unknown sigil modifier #{<<?", m, ?">>}" 82 end) 83 end 84 end