defmodule Mixpanel.Queue do
@callback new(pos_integer) :: any
@callback push(any, any) :: {:ok, any} | :discarded
@callback take(any, non_neg_integer) :: {list, any}
@callback length(any) :: non_neg_integer
end
# defmodule Mixpanel.Queue.Simple do
# @moduledoc """
# A simple queue implementation that discards elements when it's full.
# """
# @behaviour Access
# @behaviour Mixpanel.Queue
# @type prefix :: atom
# @type zipper :: %{
# length: non_neg_integer,
# head: nil | nonempty_maybe_improper_list,
# tail: nil | nonempty_maybe_improper_list
# }
# @type t :: %__MODULE__{
# max_size: non_neg_integer,
# prefixes: %{prefix => zipper}
# }
# @enforce_keys [:max_size, :prefixes]
# defstruct @enforce_keys
# @impl Mixpanel.Queue
# @spec new(pos_integer) :: t
# def new(limit) when is_integer(limit) and limit > 0 do
# %__MODULE__{
# max_size: limit,
# prefixes: %{}
# }
# end
# def new(limit),
# do: raise(ArgumentError, "limit must be greater than 0, got #{inspect(limit)}")
# @impl Mixpanel.Queue
# @spec push(t, prefix, any) :: {:ok, t} | :discarded
# def push(%__MODULE__{max_size: max_size, prefixes: prefixes} = queue, prefix, element) do
# case __MODULE__.length(queue) >= max_size do
# false when is_map_key(prefixes, prefix) ->
# prefixes =
# queue.prefixes
# |> update_in([prefix, :length], &(&1 + 1))
# |> update_in([prefix, :tail], &[element | &1])
# {:ok, %__MODULE__{queue | prefixes: prefixes}}
# false ->
# {:ok,
# %__MODULE__{
# queue
# | prefixes:
# Map.put(
# queue.prefixes,
# prefix,
# %{length: 1, head: [], tail: [element]}
# )
# }}
# true ->
# :discarded
# end
# end
# @impl Mixpanel.Queue
# @spec take(t, prefix, non_neg_integer) :: {list, t}
# def take(%__MODULE__{prefixes: prefixes} = queue, prefix, amount)
# when is_map_key(prefixes, prefix) do
# case get_in(prefixes, [prefix, :tail]) do
# [] ->
# case Enum.split(get_in(prefixes, [prefix, :head]), amount) do
# {result, []} ->
# prefixes =
# prefixes
# |> update_in([prefix, :length], fn _ -> 0 end)
# |> update_in([prefix, :head], fn _ -> [] end)
# |> update_in([prefix, :tail], fn _ -> [] end)
# {result, %__MODULE__{queue | prefixes: prefixes}}
# {result, new_head} ->
# prefixes =
# prefixes
# |> update_in([prefix, :length], &(&1 - amount))
# |> update_in([prefix, :head], fn _ -> new_head end)
# {result, %__MODULE__{queue | prefixes: prefixes}}
# end
# _ ->
# prefixes =
# prefixes
# |> update_in([prefix, :head], &(&1 ++ Enum.reverse(get_in(prefixes, [prefix, :tail]))))
# |> update_in([prefix, :tail], fn _ -> [] end)
# take(%__MODULE__{queue | prefixes: prefixes}, prefix, amount)
# end
# end
# def take(%__MODULE__{} = queue, _prefix, _amount), do: {[], queue}
# @impl Mixpanel.Queue
# @spec length(t) :: non_neg_integer
# def length(%__MODULE__{prefixes: prefixes}) do
# for {_prefix, %{length: length}} <- prefixes, reduce: 0 do
# acc -> acc + length
# end
# end
# # def at(_x, y), do: y
# # def replace_at(x, _y, _z), do: x
# # @impl Access
# # @doc false
# # def fetch(deque, index),
# # do: at(deque, index)
# # @impl Access
# # @doc false
# # def get_and_update(deque, index, fun) do
# # case at(deque, index) do
# # {:ok, current} ->
# # case fun.(current) do
# # {get, update} ->
# # {:ok, deque} = replace_at(deque, index, update)
# # {get, deque}
# # :pop ->
# # {[item], deque} = take(deque, :default, 1)
# # {item, deque}
# # end
# # _error ->
# # raise ArgumentError, "index out of bounds"
# # end
# # end
# # @impl Access
# # @doc false
# # def pop(deque, index) do
# # cond do
# # index == 0 ->
# # {[item], deque} = take(deque, :deafult, 1)
# # {item, deque}
# # index == deque.length - 1 ->
# # # TODO should pop the last item
# # {[item], deque} = take(deque, :default, 1)
# # {item, deque}
# # :else ->
# # raise ArgumentError, "removing items not at head or tail is unsupported"
# # end
# # end
# end
# defimpl Collectable, for: Mixpanel.Queue.Simple do
# @spec into(@for.t()) :: {@for.t(), (@for.t, {:cont, any} | :done | :halt -> @for.t() | :ok)}
# def into(orig) do
# {orig,
# fn
# queue, {:cont, item} ->
# case @for.push(queue, :default, item) do
# {:ok, queue} -> queue
# :discarded -> queue
# end
# queue, :done ->
# queue
# _, :halt ->
# :ok
# end}
# end
# end