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