summaryrefslogblamecommitdiff
path: root/lib/irc/plugin/temp_ref.ex
blob: 923fa1af82de558fe2316702d23e84dc5ec93aeb (plain) (tree)






































































                                                                                                                      
                  






















                                                                      
defmodule Irc.Plugin.TempRef do
  @moduledoc """
  This module allows to easily implement local temporary simple references for easy access from IRC.

  For example, your plugin output could be acted on, and instead of giving the burden for the user to
  write or copy that uuid, you could give them a small alphanumeric reference to use instead.

  You can configure how many and for how long the references are kept.

  ## Usage

  `import Irc.Plugin.TempRef`

  ```elixir
  defmodule Irc.MyPlugin do
    defstruct [:temprefs]

    def init(_) do
      #
      {:ok, %__MODULE__{temprefs: new_temp_refs()}
    end
  end
  ```
  """

  defstruct [:refs, :max, :expire, :build_fun, :build_increase_fun, :build_options]

  defmodule SimpleAlphaNumericBuilder do
    def build(options) do
      length = Keyword.get(options, :length, 3)
      for _ <- 1..length, into: "", do: <<Enum.random('bcdfghjkmpqtrvwxy2346789')>>
    end
 
    def increase(options) do
      Keyword.put(options, :length, Keyword.get(options, :length, 3) + 1)
    end
  end

  def new_temp_refs(options \\ []) do
    %__MODULE__{
      refs: Keyword.get(options, :init_refs, []),
      max: Keyword.get(options, :max, []),
      expire: Keyword.get(options, :expire, :infinity),
      build_fun: Keyword.get(options, :build_fun, &__MODULE__.SimpleAlphaNumericBuilder.build/1),
      build_increase_fun: Keyword.get(options, :build_increase_fun, &__MODULE__.SimpleAlphaNumericBuilder.increase/1),
      build_options: Keyword.get(options, :build_options, [length: 3])
    }
  end

  def janitor_refs(state = %__MODULE__{}) do
    if length(state.refs) > state.max do
      %__MODULE__{refs: state.refs |> Enum.reverse() |> tl() |> Enum.reverse()}
    else
      state
    end
  end

  def put_temp_ref(data, state = %__MODULE__{}) do
    state = janitor_refs(state)
    key = new_nonexisting_key(state)
    if key do
      ref = {key, DateTime.utc_now(), data}
      {key, %__MODULE__{state | refs: [ref | state.refs]}}
    else
      {nil, state}
    end
  end

  def lookup_temp_ref(key, state, default \\ nil) do
    case List.keyfind(state.refs, key, 0) do
      {_, _, data} -> data
      _ -> default
    end
  end

  defp new_nonexisting_key(state, i) when i > 50 do
    nil
  end

  defp new_nonexisting_key(state = %__MODULE__{refs: refs}, i \\ 1) do
    build_options = if rem(i, 5) == 0 do
      state.build_increase_fun.(state.build_options)
    else
      state.build_options
    end
    
    key = state.build_fun.(state.build_options)
    if !List.keymember?(refs, key, 0) do
      key
    else
      new_nonexisting_key(state, i + 1)
    end
  end

end