zf

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

ordered_object.ex (2700B)


      1 defmodule Jason.OrderedObject do
      2   @doc """
      3   Struct implementing a JSON object retaining order of properties.
      4 
      5   A wrapper around a keyword (that supports non-atom keys) allowing for
      6   proper protocol implementations.
      7 
      8   Implements the `Access` behaviour and `Enumerable` protocol with
      9   complexity similar to keywords/lists.
     10   """
     11 
     12   @behaviour Access
     13 
     14   @type t :: %__MODULE__{values: [{String.Chars.t(), term()}]}
     15 
     16   defstruct values: []
     17 
     18   def new(values) when is_list(values) do
     19     %__MODULE__{values: values}
     20   end
     21 
     22   @impl Access
     23   def fetch(%__MODULE__{values: values}, key) do
     24     case :lists.keyfind(key, 1, values) do
     25       {_, value} -> {:ok, value}
     26       false -> :error
     27     end
     28   end
     29 
     30   @impl Access
     31   def get_and_update(%__MODULE__{values: values} = obj, key, function) do
     32     {result, new_values} = get_and_update(values, [], key, function)
     33     {result, %{obj | values: new_values}}
     34   end
     35 
     36   @impl Access
     37   def pop(%__MODULE__{values: values} = obj, key, default \\ nil) do
     38     case :lists.keyfind(key, 1, values) do
     39       {_, value} -> {value, %{obj | values: delete_key(values, key)}}
     40       false -> {default, obj}
     41     end
     42   end
     43 
     44   defp get_and_update([{key, current} | t], acc, key, fun) do
     45     case fun.(current) do
     46       {get, value} ->
     47         {get, :lists.reverse(acc, [{key, value} | t])}
     48 
     49       :pop ->
     50         {current, :lists.reverse(acc, t)}
     51 
     52       other ->
     53         raise "the given function must return a two-element tuple or :pop, got: #{inspect(other)}"
     54     end
     55   end
     56 
     57   defp get_and_update([{_, _} = h | t], acc, key, fun), do: get_and_update(t, [h | acc], key, fun)
     58 
     59   defp get_and_update([], acc, key, fun) do
     60     case fun.(nil) do
     61       {get, update} ->
     62         {get, [{key, update} | :lists.reverse(acc)]}
     63 
     64       :pop ->
     65         {nil, :lists.reverse(acc)}
     66 
     67       other ->
     68         raise "the given function must return a two-element tuple or :pop, got: #{inspect(other)}"
     69     end
     70   end
     71 
     72   defp delete_key([{key, _} | tail], key), do: delete_key(tail, key)
     73   defp delete_key([{_, _} = pair | tail], key), do: [pair | delete_key(tail, key)]
     74   defp delete_key([], _key), do: []
     75 end
     76 
     77 defimpl Enumerable, for: Jason.OrderedObject do
     78   def count(%{values: []}), do: {:ok, 0}
     79   def count(_obj), do: {:error, __MODULE__}
     80 
     81   def member?(%{values: []}, _value), do: {:ok, false}
     82   def member?(_obj, _value), do: {:error, __MODULE__}
     83 
     84   def slice(%{values: []}), do: {:ok, 0, fn _, _ -> [] end}
     85   def slice(_obj), do: {:error, __MODULE__}
     86 
     87   def reduce(%{values: values}, acc, fun), do: Enumerable.List.reduce(values, acc, fun)
     88 end
     89 
     90 defimpl Jason.Encoder, for: Jason.OrderedObject do
     91   def encode(%{values: values}, opts) do
     92     Jason.Encode.keyword(values, opts)
     93   end
     94 end