zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

helpers.ex (2898B)


      1 defmodule Jason.Helpers do
      2   @moduledoc """
      3   Provides macro facilities for partial compile-time encoding of JSON.
      4   """
      5 
      6   alias Jason.{Codegen, Fragment}
      7 
      8   @doc ~S"""
      9   Encodes a JSON map from a compile-time keyword.
     10 
     11   Encodes the keys at compile time and strives to create as flat iodata
     12   structure as possible to achieve maximum efficiency. Does encoding
     13   right at the call site, but returns an `%Jason.Fragment{}` struct
     14   that needs to be passed to one of the "main" encoding functions -
     15   for example `Jason.encode/2` for final encoding into JSON - this
     16   makes it completely transparent for most uses.
     17 
     18   Only allows keys that do not require escaping in any of the supported
     19   encoding modes. This means only ASCII characters from the range
     20   0x1F..0x7F excluding '\', '/' and '"' are allowed - this also excludes
     21   all control characters like newlines.
     22 
     23   Preserves the order of the keys.
     24 
     25   ## Example
     26 
     27       iex> fragment = json_map(foo: 1, bar: 2)
     28       iex> Jason.encode!(fragment)
     29       "{\"foo\":1,\"bar\":2}"
     30 
     31   """
     32   defmacro json_map(kv) do
     33     kv_values = Macro.expand(kv, __CALLER__)
     34     kv_vars = Enum.map(kv_values, fn {key, _} -> {key, generated_var(key, Codegen)} end)
     35 
     36     values = Enum.map(kv_values, &elem(&1, 1))
     37     vars = Enum.map(kv_vars, &elem(&1, 1))
     38 
     39     escape = quote(do: escape)
     40     encode_map = quote(do: encode_map)
     41     encode_args = [escape, encode_map]
     42     kv_iodata = Codegen.build_kv_iodata(kv_vars, encode_args)
     43 
     44     quote do
     45       {unquote_splicing(vars)} = {unquote_splicing(values)}
     46 
     47       %Fragment{
     48         encode: fn {unquote(escape), unquote(encode_map)} ->
     49           unquote(kv_iodata)
     50         end
     51       }
     52     end
     53   end
     54 
     55   @doc ~S"""
     56   Encodes a JSON map from a variable containing a map and a compile-time
     57   list of keys.
     58 
     59   It is equivalent to calling `Map.take/2` before encoding. Otherwise works
     60   similar to `json_map/2`.
     61 
     62   ## Example
     63 
     64       iex> map = %{a: 1, b: 2, c: 3}
     65       iex> fragment = json_map_take(map, [:c, :b])
     66       iex> Jason.encode!(fragment)
     67       "{\"c\":3,\"b\":2}"
     68 
     69   """
     70   defmacro json_map_take(map, take) do
     71     take = Macro.expand(take, __CALLER__)
     72     kv = Enum.map(take, &{&1, generated_var(&1, Codegen)})
     73     escape = quote(do: escape)
     74     encode_map = quote(do: encode_map)
     75     encode_args = [escape, encode_map]
     76     kv_iodata = Codegen.build_kv_iodata(kv, encode_args)
     77 
     78     quote do
     79       case unquote(map) do
     80         %{unquote_splicing(kv)} ->
     81           %Fragment{
     82             encode: fn {unquote(escape), unquote(encode_map)} ->
     83               unquote(kv_iodata)
     84             end
     85           }
     86 
     87         other ->
     88           raise ArgumentError,
     89                 "expected a map with keys: #{unquote(inspect(take))}, got: #{inspect(other)}"
     90       end
     91     end
     92   end
     93 
     94   # The same as Macro.var/2 except it sets generated: true
     95   defp generated_var(name, context) do
     96     {name, [generated: true], context}
     97   end
     98 end