Current section
Files
Jump to
Current section
Files
lib/priority_key_server.ex
defmodule ProducerQueue.PriorityKeyServer do
@moduledoc """
## Implementation details:
iex> alias ProducerQueue.PriorityKeyServer
...> priority_keys = [:h, :l, :empty]
...> high = {3, :queue.from_list([1, 2, 3])}
...> low = {4, :queue.from_list(~w(d e f g)a)}
...> empty = {0, {[], []}}
...> state = {priority_keys, %{h: high, l: low, empty: empty}}
...> {_, [1], state} = PriorityKeyServer.handle_call({:pop, 1}, :_, state)
...> {_, [2, 3], state} = PriorityKeyServer.handle_call({:pop, 2}, :_, state)
...> {_, _, state} = PriorityKeyServer.handle_call({:push, {:h, [4]}}, :_, state)
...> {_, [4], _state} = PriorityKeyServer.handle_call({:pop, 1}, :_, state)
iex> alias ProducerQueue.PriorityKeyServer
...> priority_keys = [:h, :l, :empty]
...> high = {3, :queue.from_list([1, 2, 3])}
...> low = {4, :queue.from_list(~w(d e f g)a)}
...> empty = {0, {[], []}}
...> state = {priority_keys, %{h: high, l: low, empty: empty}}
...> {_, [], state} = PriorityKeyServer.handle_call({:pop, 0}, :_, state)
...> {_, [1, 2, 3, :d, :e, :f, :g], _} = PriorityKeyServer.handle_call({:pop, 10}, :_, state)
iex> alias ProducerQueue.PriorityKeyServer
...> priority_keys = [:h, :l]
...> empty = {0, {[], []}}
...> state = {priority_keys, %{h: empty, l: empty}}
...> {:noreply, state} = PriorityKeyServer.handle_cast({:push, {:_, [:a]}}, state)
...> {_, [:a], _} = PriorityKeyServer.handle_call({:pop, 1}, :_, state)
iex> alias ProducerQueue.PriorityKeyServer
...> {_, {[:a, :b, :c], _}} = PriorityKeyServer.init(priority: [:a, :b, :c])
"""
use GenServer
@q {0, {[], []}}
def init(opts), do: init(:priority, Keyword.get(opts, :priority, [:default]))
def init(:priority, prio), do: {:ok, {prio, Enum.into(prio, %{}, &{&1, @q})}}
def handle_call({:pop, count}, _, state), do: out(count, state)
def handle_call({:push, list}, _, state), do: {:reply, :ok, qin(list, state)}
def handle_cast({:push, list}, state), do: {:noreply, qin(list, state)}
defp out(i, state), do: out(i, state, _acc = [], _prio_list = elem(state, 0))
defp out(0, state, acc, _), do: {:reply, Enum.reverse(acc), state}
defp out(_, state, acc, []), do: out(0, state, acc, _empty_prio_list = [])
defp out(i, {_, m} = s, acc, [hd | tl]), do: out(i, s, acc, [hd | tl], m[hd])
defp out(i, {p, m}, l, [k | t], @q), do: out(i, {p, Map.put(m, k, @q)}, l, t)
defp out(0, {p, m}, l, [k | _], q), do: out(0, {p, Map.put(m, k, q)}, l, [])
defp out(i, s, l, p, {c, q}), do: out(i - 1, s, l, p, {c, q}, :queue.out(q))
defp out(i, s, l, p, {c, _}, {{_, v}, q}), do: out(i, s, [v | l], p, {c - 1, q})
defp qin({:_, list}, {prio, map}), do: qin({hd(prio), list}, {prio, map})
defp qin({k, l}, {p, %{} = m}), do: qin(p, m, k, Enum.reduce(l, m[k], &qin/2))
defp qin(item, {size, queue} = _acc), do: {size + 1, :queue.in(item, queue)}
defp qin(p, m, k, acc), do: {p, Map.put(m, k, acc)}
end