Do not run remote converger if no deps will use it

Closes #4833.

# This module is the one responsible for converging
# dependencies in a recursive fashion. This
# module and its functions are private to Mix.
defmodule Mix.Dep.Converger do
  @moduledoc false

  @doc """
  Topsorts the given dependencies.
  """
  def topsort(deps) do
    graph = :digraph.new

    try do
      Enum.each(deps, fn %Mix.Dep{app: app} ->
        :digraph.add_vertex(graph, app)
      end)

      Enum.each(deps, fn %Mix.Dep{app: app, deps: other_deps} ->
        Enum.each(other_deps, fn
          %Mix.Dep{app: ^app} ->
            Mix.raise "App #{app} lists itself as a dependency"
          %Mix.Dep{app: other_app} ->
            :digraph.add_edge(graph, other_app, app)
        end)
      end)

      if apps = :digraph_utils.topsort(graph) do
        Enum.map(apps, fn(app) ->
          Enum.find(deps, fn(%Mix.Dep{app: other_app}) -> app == other_app end)
        end)
      else
        Mix.raise "Could not sort dependencies. There are cycles in the dependency graph"
      end
    after
      :digraph.delete(graph)
    end
  end

  @doc """
  Converges all dependencies from the current project,
  including nested dependencies.

  There is a callback that is invoked for each dependency and
  must return an updated dependency in case some processing
  is done.

  See `Mix.Dep.Loader.children/1` for options.
  """
  def converge(acc, lock, opts, callback) do
    {deps, acc, lock} = all(acc, lock, opts, callback)
    {topsort(deps), acc, lock}
  end

  defp all(acc, lock, opts, callback) do
    main = Mix.Dep.Loader.children()
    main = Enum.map(main, &(%{&1 | top_level: true}))
    apps = Enum.map(main, &(&1.app))

    lock_given? = !!lock
    env = opts[:env]

    # If no lock was given, let's read one to fill in the deps
    lock = lock || Mix.Dep.Lock.read

    # Run converger for all dependencies, except remote
    # dependencies. Since the remote converger may be
    # lazily loaded, we need to check for it on every
    # iteration.
    {deps, rest, lock} =
      all(main, apps, callback, acc, lock, env, fn dep ->
        if (remote = Mix.RemoteConverger.get) && remote.remote?(dep) do
          {:loaded, dep}
        else
          {:unloaded, dep, nil}
        end
      end)

    # Run remote converger and rerun Mix's converger with the new information.
    # Don't run the remote if deps didn't converge, if the remote is not
    # available or if none of the deps are handled by the remote.
    remote = Mix.RemoteConverger.get
    diverged? = Enum.any?(deps, &Mix.Dep.diverged?/1)
    use_remote? = !!remote and Enum.any?(deps, &remote.remote?/1)

    if not diverged? and use_remote? do
      # If there is a lock, it means we are doing a get/update
      # and we need to hit the remote converger which do external
      # requests and what not. In case of deps.loadpths, deps and so
      # on, there is no lock, so we won't hit this branch.
      lock = if lock_given?, do: remote.converge(deps, lock), else: lock

      deps = deps
             |> Enum.reject(&remote.remote?(&1))
             |> Enum.into(%{}, &{&1.app, &1})

      # In case no lock was given, we will use the local lock
      # which is potentially stale. So remote.deps/2 needs to always
      # check if the data it finds in the lock is actually valid.
      {deps, rest, lock} =
        all(main, apps, callback, rest, lock, env, fn dep ->
          if cached = deps[dep.app] do
            {:loaded, cached}
          else
            {:unloaded, dep, remote.deps(dep, lock)}
          end
        end)

      {reject_non_fulfilled_optional(deps), rest, lock}
    else
      {reject_non_fulfilled_optional(deps), rest, lock}
    end
  end

  defp all(main, apps, callback, rest, lock, env, cache) do
    {deps, rest, lock} = all(main, [], [], apps, callback, rest, lock, env, cache)
    # When traversing dependencies, we keep skipped ones to
    # find conflicts. We remove them now after traversal.
    {deps, _} = Mix.Dep.Loader.partition_by_env(deps, env)
    {deps, rest, lock}
  end

  # We traverse the tree of dependencies in a breadth-first
  # fashion. The reason for this is that we converge
  # dependencies, but allow the parent to override any
  # dependency in the child. Consider this tree with
  # dependencies "a", "b", etc and the order they are
  # converged:
  #
  #     * project
  #       1) a
  #       2) b
  #         4) d
  #       3) c
  #         5) e
  #         6) f
  #           7) d
  #
  # Notice that the "d" dependency exists as a child of "b"
  # and child of "f". In case the dependency is the same,
  # we proceed. However, if there is a conflict, for instance
  # different Git repositories are used as source in each, we
  # raise an exception.
  #
  # In order to solve such dependencies, we allow the project
  # to specify the same dependency, but it will be considered
  # to have higher priority:
  #
  #     * project
  #       1) a
  #       2) b
  #         5) d
  #       3) c
  #         6) e
  #         7) f
  #           8) d
  #       4) d
  #
  # Now, since "d" was specified in a parent project, no
  # exception is going to be raised since d is considered
  # to be the authoritative source.
  defp all([dep | t], acc, upper_breadths, current_breadths, callback, rest, lock, env, cache) do
    cond do
      new_acc = diverged_deps(acc, upper_breadths, dep) ->
        all(t, new_acc, upper_breadths, current_breadths, callback, rest, lock, env, cache)
      Mix.Dep.Loader.skip?(dep, env) ->
        # We still keep skipped dependencies around to detect conflicts.
        # They must be rejected after every all iteration.
        all(t, [dep | acc], upper_breadths, current_breadths, callback, rest, lock, env, cache)
      true ->
        {dep, rest, lock} =
          case cache.(dep) do
            {:loaded, cached_dep} ->
              {cached_dep, rest, lock}
            {:unloaded, dep, children} ->
              {dep, rest, lock} = callback.(put_lock(dep, lock), rest, lock)

              # After we invoke the callback (which may actually check out the
              # dependency), we load the dependency including its latest info
              # and children information.
              {Mix.Dep.Loader.load(dep, children), rest, lock}
          end

        {acc, rest, lock} =
          all(t, [dep | acc], upper_breadths, current_breadths, callback, rest, lock, env, cache)

        deps = reject_non_fulfilled_optional(dep.deps, Enum.map(acc, & &1.app))
        new_breadths = Enum.map(deps, &(&1.app)) ++ current_breadths
        all(deps, acc, current_breadths, new_breadths, callback, rest, lock, env, cache)
    end
  end

  defp all([], acc, _upper, _current, _callback, rest, lock, _env, _cache) do
    {Enum.reverse(acc), rest, lock}
  end

  defp put_lock(%Mix.Dep{app: app} = dep, lock) do
    put_in dep.opts[:lock], lock[app]
  end

  # Look for divergence in dependencies.
  #
  # If the same dependency is specified more than once,
  # we need to guarantee they converge. If they don't
  # converge, we mark them as diverged.
  #
  # The only exception is when the dependency that
  # diverges is in the upper breadth, in those cases we
  # also check for the override option and mark the dependency
  # as overridden instead of diverged.
  defp diverged_deps(list, upper_breadths, %Mix.Dep{app: app} = dep) do
    in_upper? = app in upper_breadths

    {acc, match} =
      Enum.map_reduce list, false, fn(other, match) ->
        %Mix.Dep{app: other_app, opts: other_opts} = other

        cond do
          app != other_app ->
            {other, match}
          in_upper? && other_opts[:override] ->
            {other |> with_matching_only(dep, in_upper?), true}
          converge?(other, dep) ->
            {other |> with_matching_only(dep, in_upper?)
                   |> with_matching_req(dep) |> merge_manager(dep, in_upper?), true}
          true ->
            tag = if in_upper?, do: :overridden, else: :diverged
            {%{other | status: {tag, dep}}, true}
        end
      end

    if match, do: acc
  end

  defp with_matching_only(%{opts: other_opts} = other, %{opts: opts} = dep, in_upper?) do
    if opts[:optional] do
      other
    else
      with_matching_only(other, other_opts, dep, opts, in_upper?)
    end
  end

  # When in_upper is true
  #
  # When a parent dependency specifies :only that is a subset
  # of a child dependency, we are going to abort as the parent
  # dependency must explicitly outline a superset of child
  # dependencies.
  #
  # We could resolve such conflicts automatically but, since
  # the user has likely written only: :env in their mix.exs
  # file, we decided to go with a more explicit approach of
  # asking them to change it to avoid later surprises and
  # headaches.
  defp with_matching_only(other, other_opts, dep, opts, true) do
    case Keyword.fetch(other_opts, :only) do
      {:ok, other_only} ->
        case Keyword.fetch(opts, :only) do
          {:ok, only} ->
            case List.wrap(only) -- List.wrap(other_only) do
              [] -> other
              _  -> %{other | status: {:divergedonly, dep}}
            end
          :error ->
            %{other | status: {:divergedonly, dep}}
        end
      :error ->
        other
    end
  end

  # When in_upper is false
  #
  # In this case, the two dependencies do not have a common path and
  # only solution is to merge the environments. We have decided to
  # perform it explicitly as, opposite to in_upper above, the
  # dependencies are never really laid out in the parent tree.
  defp with_matching_only(other, other_opts, _dep, opts, false) do
    other_only = Keyword.get(other_opts, :only)
    only = Keyword.get(opts, :only)
    if other_only && only do
      put_in other.opts[:only], Enum.uniq(List.wrap(other_only) ++ List.wrap(only))
    else
      %{other | opts: Keyword.delete(other_opts, :only)}
    end
  end

  defp converge?(%Mix.Dep{scm: scm1, opts: opts1}, %Mix.Dep{scm: scm2, opts: opts2}) do
    scm1 == scm2 and opts_equal?(opts1, opts2) and scm1.equal?(opts1, opts2)
  end

  defp opts_equal?(opts1, opts2) do
    keys = ~w(app env compile)a
    Enum.all?(keys, &(Keyword.fetch(opts1, &1) == Keyword.fetch(opts2, &1)))
  end

  defp reject_non_fulfilled_optional(deps) do
    apps = Enum.map(deps, & &1.app)
    for dep <- deps do
      update_in dep.deps, &reject_non_fulfilled_optional(&1, apps)
    end
  end

  defp reject_non_fulfilled_optional(children, upper_breadths) do
    Enum.reject children, fn %Mix.Dep{app: app, opts: opts} ->
      opts[:optional] && not(app in upper_breadths)
    end
  end

  defp merge_manager(%{manager: other_manager} = other, %{manager: manager}, in_upper?) do
    %{other | manager: sort_manager(other_manager, manager, in_upper?)}
  end

  @managers [:mix, :rebar3, :rebar, :make]

  defp sort_manager(other_manager, manager, true) do
    other_manager || manager
  end
  defp sort_manager(other_manager, manager, false) do
    priority = @managers -- (@managers -- (List.wrap(other_manager) ++ List.wrap(manager)))
    List.first(priority) || other_manager || manager
  end

  defp with_matching_req(%Mix.Dep{} = other, %Mix.Dep{} = dep) do
    case other.status do
      {:ok, vsn} when not is_nil(vsn) ->
        case Mix.Dep.Loader.vsn_match(dep.requirement, vsn, dep.app) do
          {:ok, true} -> other
          _ -> %{other | status: {:divergedreq, vsn, dep}}
        end
      _ ->
        other
    end
  end
end

# This module is the one responsible for converging
# dependencies in a recursive fashion. This
# module and its functions are private to Mix.
defmodule Mix.Dep.Converger do
  @moduledoc false

  @doc """
  Topsorts the given dependencies.
  """
  def topsort(deps) do
    graph = :digraph.new

    try do
      Enum.each(deps, fn %Mix.Dep{app: app} ->
        :digraph.add_vertex(graph, app)
      end)

      Enum.each(deps, fn %Mix.Dep{app: app, deps: other_deps} ->
        Enum.each(other_deps, fn
          %Mix.Dep{app: ^app} ->
            Mix.raise "App #{app} lists itself as a dependency"
          %Mix.Dep{app: other_app} ->
            :digraph.add_edge(graph, other_app, app)
        end)
      end)

      if apps = :digraph_utils.topsort(graph) do
        Enum.map(apps, fn(app) ->
          Enum.find(deps, fn(%Mix.Dep{app: other_app}) -> app == other_app end)
        end)
      else
        Mix.raise "Could not sort dependencies. There are cycles in the dependency graph"
      end
    after
      :digraph.delete(graph)
    end
  end

  @doc """
  Converges all dependencies from the current project,
  including nested dependencies.

  There is a callback that is invoked for each dependency and
  must return an updated dependency in case some processing
  is done.

  See `Mix.Dep.Loader.children/1` for options.
  """
  def converge(acc, lock, opts, callback) do
    {deps, acc, lock} = all(acc, lock, opts, callback)
    {topsort(deps), acc, lock}
  end

  defp all(acc, lock, opts, callback) do
    main = Mix.Dep.Loader.children()
    main = Enum.map(main, &(%{&1 | top_level: true}))
    apps = Enum.map(main, &(&1.app))

    lock_given? = !!lock
    env = opts[:env]

    # If no lock was given, let's read one to fill in the deps
    lock = lock || Mix.Dep.Lock.read

    # Run converger for all dependencies, except remote
    # dependencies. Since the remote converger may be
    # lazily loaded, we need to check for it on every
    # iteration.
    {deps, rest, lock} =
      all(main, apps, callback, acc, lock, env, fn dep ->
        if (remote = Mix.RemoteConverger.get) && remote.remote?(dep) do
          {:loaded, dep}
        else
          {:unloaded, dep, nil}
        end
      end)

    diverged? = Enum.any?(deps, &Mix.Dep.diverged?/1)

    # Run remote converger if one is available and rerun Mix's
    # converger with the new information
    remote = Mix.RemoteConverger.get

    if not diverged? && remote do
      # If there is a lock, it means we are doing a get/update
      # and we need to hit the remote converger which do external
      # requests and what not. In case of deps.loadpths, deps and so
      # on, there is no lock, so we won't hit this branch.
      lock = if lock_given?, do: remote.converge(deps, lock), else: lock

      deps = deps
             |> Enum.reject(&remote.remote?(&1))
             |> Enum.into(%{}, &{&1.app, &1})

      # In case no lock was given, we will use the local lock
      # which is potentially stale. So remote.deps/2 needs to always
      # check if the data it finds in the lock is actually valid.
      {deps, rest, lock} =
        all(main, apps, callback, rest, lock, env, fn dep ->
          if cached = deps[dep.app] do
            {:loaded, cached}
          else
            {:unloaded, dep, remote.deps(dep, lock)}
          end
        end)

      {reject_non_fulfilled_optional(deps), rest, lock}
    else
      {reject_non_fulfilled_optional(deps), rest, lock}
    end
  end

  defp all(main, apps, callback, rest, lock, env, cache) do
    {deps, rest, lock} = all(main, [], [], apps, callback, rest, lock, env, cache)
    # When traversing dependencies, we keep skipped ones to
    # find conflicts. We remove them now after traversal.
    {deps, _} = Mix.Dep.Loader.partition_by_env(deps, env)
    {deps, rest, lock}
  end

  # We traverse the tree of dependencies in a breadth-first
  # fashion. The reason for this is that we converge
  # dependencies, but allow the parent to override any
  # dependency in the child. Consider this tree with
  # dependencies "a", "b", etc and the order they are
  # converged:
  #
  #     * project
  #       1) a
  #       2) b
  #         4) d
  #       3) c
  #         5) e
  #         6) f
  #           7) d
  #
  # Notice that the "d" dependency exists as a child of "b"
  # and child of "f". In case the dependency is the same,
  # we proceed. However, if there is a conflict, for instance
  # different Git repositories are used as source in each, we
  # raise an exception.
  #
  # In order to solve such dependencies, we allow the project
  # to specify the same dependency, but it will be considered
  # to have higher priority:
  #
  #     * project
  #       1) a
  #       2) b
  #         5) d
  #       3) c
  #         6) e
  #         7) f
  #           8) d
  #       4) d
  #
  # Now, since "d" was specified in a parent project, no
  # exception is going to be raised since d is considered
  # to be the authoritative source.
  defp all([dep | t], acc, upper_breadths, current_breadths, callback, rest, lock, env, cache) do
    cond do
      new_acc = diverged_deps(acc, upper_breadths, dep) ->
        all(t, new_acc, upper_breadths, current_breadths, callback, rest, lock, env, cache)
      Mix.Dep.Loader.skip?(dep, env) ->
        # We still keep skipped dependencies around to detect conflicts.
        # They must be rejected after every all iteration.
        all(t, [dep | acc], upper_breadths, current_breadths, callback, rest, lock, env, cache)
      true ->
        {dep, rest, lock} =
          case cache.(dep) do
            {:loaded, cached_dep} ->
              {cached_dep, rest, lock}
            {:unloaded, dep, children} ->
              {dep, rest, lock} = callback.(put_lock(dep, lock), rest, lock)

              # After we invoke the callback (which may actually check out the
              # dependency), we load the dependency including its latest info
              # and children information.
              {Mix.Dep.Loader.load(dep, children), rest, lock}
          end

        {acc, rest, lock} =
          all(t, [dep | acc], upper_breadths, current_breadths, callback, rest, lock, env, cache)

        deps = reject_non_fulfilled_optional(dep.deps, Enum.map(acc, & &1.app))
        new_breadths = Enum.map(deps, &(&1.app)) ++ current_breadths
        all(deps, acc, current_breadths, new_breadths, callback, rest, lock, env, cache)
    end
  end

  defp all([], acc, _upper, _current, _callback, rest, lock, _env, _cache) do
    {Enum.reverse(acc), rest, lock}
  end

  defp put_lock(%Mix.Dep{app: app} = dep, lock) do
    put_in dep.opts[:lock], lock[app]
  end

  # Look for divergence in dependencies.
  #
  # If the same dependency is specified more than once,
  # we need to guarantee they converge. If they don't
  # converge, we mark them as diverged.
  #
  # The only exception is when the dependency that
  # diverges is in the upper breadth, in those cases we
  # also check for the override option and mark the dependency
  # as overridden instead of diverged.
  defp diverged_deps(list, upper_breadths, %Mix.Dep{app: app} = dep) do
    in_upper? = app in upper_breadths

    {acc, match} =
      Enum.map_reduce list, false, fn(other, match) ->
        %Mix.Dep{app: other_app, opts: other_opts} = other

        cond do
          app != other_app ->
            {other, match}
          in_upper? && other_opts[:override] ->
            {other |> with_matching_only(dep, in_upper?), true}
          converge?(other, dep) ->
            {other |> with_matching_only(dep, in_upper?)
                   |> with_matching_req(dep) |> merge_manager(dep, in_upper?), true}
          true ->
            tag = if in_upper?, do: :overridden, else: :diverged
            {%{other | status: {tag, dep}}, true}
        end
      end

    if match, do: acc
  end

  defp with_matching_only(%{opts: other_opts} = other, %{opts: opts} = dep, in_upper?) do
    if opts[:optional] do
      other
    else
      with_matching_only(other, other_opts, dep, opts, in_upper?)
    end
  end

  # When in_upper is true
  #
  # When a parent dependency specifies :only that is a subset
  # of a child dependency, we are going to abort as the parent
  # dependency must explicitly outline a superset of child
  # dependencies.
  #
  # We could resolve such conflicts automatically but, since
  # the user has likely written only: :env in their mix.exs
  # file, we decided to go with a more explicit approach of
  # asking them to change it to avoid later surprises and
  # headaches.
  defp with_matching_only(other, other_opts, dep, opts, true) do
    case Keyword.fetch(other_opts, :only) do
      {:ok, other_only} ->
        case Keyword.fetch(opts, :only) do
          {:ok, only} ->
            case List.wrap(only) -- List.wrap(other_only) do
              [] -> other
              _  -> %{other | status: {:divergedonly, dep}}
            end
          :error ->
            %{other | status: {:divergedonly, dep}}
        end
      :error ->
        other
    end
  end

  # When in_upper is false
  #
  # In this case, the two dependencies do not have a common path and
  # only solution is to merge the environments. We have decided to
  # perform it explicitly as, opposite to in_upper above, the
  # dependencies are never really laid out in the parent tree.
  defp with_matching_only(other, other_opts, _dep, opts, false) do
    other_only = Keyword.get(other_opts, :only)
    only = Keyword.get(opts, :only)
    if other_only && only do
      put_in other.opts[:only], Enum.uniq(List.wrap(other_only) ++ List.wrap(only))
    else
      %{other | opts: Keyword.delete(other_opts, :only)}
    end
  end

  defp converge?(%Mix.Dep{scm: scm1, opts: opts1}, %Mix.Dep{scm: scm2, opts: opts2}) do
    scm1 == scm2 and opts_equal?(opts1, opts2) and scm1.equal?(opts1, opts2)
  end

  defp opts_equal?(opts1, opts2) do
    keys = ~w(app env compile)a
    Enum.all?(keys, &(Keyword.fetch(opts1, &1) == Keyword.fetch(opts2, &1)))
  end

  defp reject_non_fulfilled_optional(deps) do
    apps = Enum.map(deps, & &1.app)
    for dep <- deps do
      update_in dep.deps, &reject_non_fulfilled_optional(&1, apps)
    end
  end

  defp reject_non_fulfilled_optional(children, upper_breadths) do
    Enum.reject children, fn %Mix.Dep{app: app, opts: opts} ->
      opts[:optional] && not(app in upper_breadths)
    end
  end

  defp merge_manager(%{manager: other_manager} = other, %{manager: manager}, in_upper?) do
    %{other | manager: sort_manager(other_manager, manager, in_upper?)}
  end

  @managers [:mix, :rebar3, :rebar, :make]

  defp sort_manager(other_manager, manager, true) do
    other_manager || manager
  end
  defp sort_manager(other_manager, manager, false) do
    priority = @managers -- (@managers -- (List.wrap(other_manager) ++ List.wrap(manager)))
    List.first(priority) || other_manager || manager
  end

  defp with_matching_req(%Mix.Dep{} = other, %Mix.Dep{} = dep) do
    case other.status do
      {:ok, vsn} when not is_nil(vsn) ->
        case Mix.Dep.Loader.vsn_match(dep.requirement, vsn, dep.app) do
          {:ok, true} -> other
          _ -> %{other | status: {:divergedreq, vsn, dep}}
        end
      _ ->
        other
    end
  end
end


version 0.7.3

version 0.7.3


Remove dashboard handler from PageController #40, #52


order episode by title in admin

order episode by title in admin

defmodule Pan.PodcastController do
  use Pan.Web, :controller
  alias Pan.Episode

  alias Pan.Podcast

  plug :scrub_params, "podcast" when action in [:create, :update]

  def index(conn, _params) do
    podcasts = Repo.all(from p in Podcast, order_by: [desc: :id])
    render(conn, "index.html", podcasts: podcasts)
  end

  def new(conn, _params) do
    changeset = Podcast.changeset(%Podcast{})
    render(conn, "new.html", changeset: changeset)
  end

  def create(conn, %{"podcast" => podcast_params}) do
    changeset = Podcast.changeset(%Podcast{}, podcast_params)

    case Repo.insert(changeset) do
      {:ok, _podcast} ->
        conn
        |> put_flash(:info, "Podcast created successfully.")
        |> redirect(to: podcast_path(conn, :index))
      {:error, changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end

  def show(conn, %{"id" => id}) do
    podcast = Repo.get!(Podcast, id)
              |> Repo.preload(episodes: from(e in Episode, order_by: e.title))
              |> Repo.preload(episodes: :podcast)
              |> Repo.preload(feeds: :podcast)
              |> Repo.preload([:languages, :owner, :categories])
    render(conn, "show.html", podcast: podcast)
  end

  def edit(conn, %{"id" => id}) do
    podcast = Repo.get!(Podcast, id)
    changeset = Podcast.changeset(podcast)
    render(conn, "edit.html", podcast: podcast, changeset: changeset)
  end

  def update(conn, %{"id" => id, "podcast" => podcast_params}) do
    podcast = Repo.get!(Podcast, id)
    changeset = Podcast.changeset(podcast, podcast_params)

    case Repo.update(changeset) do
      {:ok, podcast} ->
        conn
        |> put_flash(:info, "Podcast updated successfully.")
        |> redirect(to: podcast_path(conn, :show, podcast))
      {:error, changeset} ->
        render(conn, "edit.html", podcast: podcast, changeset: changeset)
    end
  end

  def delete(conn, %{"id" => id}) do
    podcast = Repo.get!(Podcast, id)
              |> Repo.preload(:episodes)

    for episode <- podcast.episodes do
      Repo.delete!(episode)
    end

    # Here we use delete! (with a bang) because we expect
    # it to always work (and if it does not, it will raise).
    Repo.delete!(podcast)

    conn
    |> put_flash(:info, "Podcast deleted successfully.")
    |> redirect(to: podcast_path(conn, :index))
  end
end

defmodule Pan.PodcastController do
  use Pan.Web, :controller

  alias Pan.Podcast

  plug :scrub_params, "podcast" when action in [:create, :update]

  def index(conn, _params) do
    podcasts = Repo.all(from p in Podcast, order_by: [desc: :id])
    render(conn, "index.html", podcasts: podcasts)
  end

  def new(conn, _params) do
    changeset = Podcast.changeset(%Podcast{})
    render(conn, "new.html", changeset: changeset)
  end

  def create(conn, %{"podcast" => podcast_params}) do
    changeset = Podcast.changeset(%Podcast{}, podcast_params)

    case Repo.insert(changeset) do
      {:ok, _podcast} ->
        conn
        |> put_flash(:info, "Podcast created successfully.")
        |> redirect(to: podcast_path(conn, :index))
      {:error, changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end

  def show(conn, %{"id" => id}) do
    podcast = Repo.get!(Podcast, id)
              |> Repo.preload(episodes: :podcast)
              |> Repo.preload(feeds: :podcast)
              |> Repo.preload([:languages, :owner, :categories])
    render(conn, "show.html", podcast: podcast)
  end

  def edit(conn, %{"id" => id}) do
    podcast = Repo.get!(Podcast, id)
    changeset = Podcast.changeset(podcast)
    render(conn, "edit.html", podcast: podcast, changeset: changeset)
  end

  def update(conn, %{"id" => id, "podcast" => podcast_params}) do
    podcast = Repo.get!(Podcast, id)
    changeset = Podcast.changeset(podcast, podcast_params)

    case Repo.update(changeset) do
      {:ok, podcast} ->
        conn
        |> put_flash(:info, "Podcast updated successfully.")
        |> redirect(to: podcast_path(conn, :show, podcast))
      {:error, changeset} ->
        render(conn, "edit.html", podcast: podcast, changeset: changeset)
    end
  end

  def delete(conn, %{"id" => id}) do
    podcast = Repo.get!(Podcast, id)
              |> Repo.preload(:episodes)

    for episode <- podcast.episodes do
      Repo.delete!(episode)
    end

    # Here we use delete! (with a bang) because we expect
    # it to always work (and if it does not, it will raise).
    Repo.delete!(podcast)

    conn
    |> put_flash(:info, "Podcast deleted successfully.")
    |> redirect(to: podcast_path(conn, :index))
  end
end


Removing useless redirects on login errors

defmodule Academy.SessionController do
  use Academy.Web, :controller
  alias Academy.User

  require Logger

  def new(conn, _params) do
    if logged_in?(conn) do
      conn
        |> put_flash(:warn, "You already are logged in")
        |> redirect(to: user_path(conn, :index))
    else
      render conn, "login.html"
    end
  end

  def create(conn, %{"session" => session_params}) do
    if logged_in?(conn) do
      conn
        |> put_flash(:warn, "You already are logged in")
        |> redirect(to: user_path(conn, :index))
    else
      case login(session_params) do
        {:ok, username} ->
          handle_login(conn, username)
        {:error, reason} ->
          handle_error(conn, reason)
      end
    end
  end

  def delete(conn, _) do
    if logged_in?(conn) do
      conn
      |> logout
      |> put_flash(:info, "Logged out")
      |> redirect(to: user_path(conn, :index))
    else
      conn
      |> put_flash(:error, "You are not logged in")
      |> redirect(to: user_path(conn, :index))
    end
  end

  defp handle_login(conn, username) do
    user = User.get_or_create(username)
    conn
    |> Guardian.Plug.sign_in(user)
    |> put_flash(:info, "You are now logged in as #{String.capitalize user.name}")
    |> redirect(to: user_path(conn, :index))
  end

  defp handle_error(conn, error) do
    conn = case error do
      :missing_field ->
        put_flash(conn, :error, "Please ensure all required fields are filled")
      :invalid_credentials ->
        put_flash(conn, :error, "Wrong username or password")
      _ ->
        put_flash(conn, :error, "Internal error. Please try again.")
    end
    render conn, "login.html"
  end

  def login(%{"username" => username, "password" => password}) when username != "" and password != "" do
    case Academy.Endpoint.LDAP.check_credentials(username, password) do
      :ok ->
        Logger.info("User #{username} successfully authenticated to LDAP")
        {:ok, username}
      {:error, :invalid_credentials} ->
        Logger.info("User #{username} failed to authenticate to LDAP: Wrong login/password.")
        {:error, :invalid_credentials}
      {:error, error_msg} ->
        Logger.warn("User #{username} failed to authenticate to LDAP: #{error_msg}")
        {:error, error_msg}
    end
  end

  def login(_params), do: {:error, :missing_field}

  def logout(conn) do
    Guardian.Plug.sign_out(conn)
  end

  def current_user(conn) do
    Guardian.Plug.current_resource(conn)
  end

  def logged_in?(conn) do
    !!current_user(conn)
  end

end

defmodule Academy.SessionController do
  use Academy.Web, :controller
  alias Academy.User

  require Logger

  def new(conn, _params) do
    if logged_in?(conn) do
      conn
        |> put_flash(:warn, "You already are logged in")
        |> redirect(to: user_path(conn, :index))
    else
      render conn, "login.html"
    end
  end

  def create(conn, %{"session" => session_params}) do
    if logged_in?(conn) do
      conn
        |> put_flash(:warn, "You already are logged in")
        |> redirect(to: user_path(conn, :index))
    else
      case login(session_params) do
        {:ok, username} ->
          handle_login(conn, username)
        {:error, reason} ->
          handle_error(conn, reason)
      end
    end
  end

  def delete(conn, _) do
    if logged_in?(conn) do
      conn
      |> logout
      |> put_flash(:info, "Logged out")
      |> redirect(to: user_path(conn, :index))
    else
      conn
      |> put_flash(:error, "You are not logged in")
      |> redirect(to: user_path(conn, :index))
    end
  end

  defp handle_login(conn, username) do
    user = User.get_or_create(username)
    conn
    |> Guardian.Plug.sign_in(user)
    |> put_flash(:info, "You are now logged in as #{String.capitalize user.name}")
    |> redirect(to: user_path(conn, :index))
  end

  defp handle_error(conn, error) do
    case error do
      :missing_field ->
        conn
        |> put_flash(:error, "Please ensure all required fields are filled")
        |> redirect(to: session_path(conn, :new))
      :invalid_credentials ->
        conn
        |> put_flash(:error, "Wrong username or password")
        |> redirect(to: session_path(conn, :new))
      _ ->
        conn
        |> put_flash(:error, "Internal error. Please try again.")
        |> redirect(to: session_path(conn, :new))
    end
  end

  def login(%{"username" => username, "password" => password}) when username != "" and password != "" do
    case Academy.Endpoint.LDAP.check_credentials(username, password) do
      :ok ->
        Logger.info("User #{username} successfully authenticated to LDAP")
        {:ok, username}
      {:error, :invalid_credentials} ->
        Logger.info("User #{username} failed to authenticate to LDAP: Wrong login/password.")
        {:error, :invalid_credentials}
      {:error, error_msg} ->
        Logger.warn("User #{username} failed to authenticate to LDAP: #{error_msg}")
        {:error, error_msg}
    end
  end

  def login(_params), do: {:error, :missing_field}

  def logout(conn) do
    Guardian.Plug.sign_out(conn)
  end

  def current_user(conn) do
    Guardian.Plug.current_resource(conn)
  end

  def logged_in?(conn) do
    !!current_user(conn)
  end

end


Avoid Dialyzer warnings on Inspect (#8841)

Closes #8749.

import Kernel, except: [inspect: 1]
import Inspect.Algebra

alias Code.Identifier

defprotocol Inspect do
  @moduledoc """
  The `Inspect` protocol converts an Elixir data structure into an
  algebra document.

  This documentation refers to implementing the `Inspect` protocol
  for your own data structures. To learn more about using inspect,
  see `Kernel.inspect/2` and `IO.inspect/2`.

  The `inspect/2` function receives the entity to be inspected
  followed by the inspecting options, represented by the struct
  `Inspect.Opts`. Building of the algebra document is done with
  `Inspect.Algebra`.

  ## Examples

  Many times, inspecting a structure can be implemented in function
  of existing entities. For example, here is `MapSet`'s `inspect/2`
  implementation:

      defimpl Inspect, for: MapSet do
        import Inspect.Algebra

        def inspect(dict, opts) do
          concat(["#MapSet<", to_doc(MapSet.to_list(dict), opts), ">"])
        end
      end

  The `concat/1` function comes from `Inspect.Algebra` and it
  concatenates algebra documents together. In the example above,
  it is concatenating the string `"MapSet<"` (all strings are
  valid algebra documents that keep their formatting when pretty
  printed), the document returned by `Inspect.Algebra.to_doc/2` and the
  other string `">"`.

  Since regular strings are valid entities in an algebra document,
  an implementation of the `Inspect` protocol may simply return a
  string, although that will devoid it of any pretty-printing.

  ## Error handling

  In case there is an error while your structure is being inspected,
  Elixir will raise an `ArgumentError` error and will automatically fall back
  to a raw representation for printing the structure.

  You can however access the underlying error by invoking the `Inspect`
  implementation directly. For example, to test `Inspect.MapSet` above,
  you can invoke it as:

      Inspect.MapSet.inspect(MapSet.new(), %Inspect.Opts{})

  ## Deriving

  The `Inspect` protocol can be derived to hide certain fields from
  structs, so they don't show up in logs, inspects and similar. This
  is especially useful for fields containing private information.

  The options `:only` and `:except` can be used with `@derive` to
  specify which fields should and should not appear in the
  algebra document:

      defmodule User do
        @derive {Inspect, only: [:id, :name]}
        defstruct [:id, :name, :address]
      end

      inspect(%User{id: 1, name: "Homer", address: "742 Evergreen Terrace"})
      #=> #User<id: 1, name: "Homer", ...>

  """

  # Handle structs in Any
  @fallback_to_any true

  def inspect(term, opts)
end

defimpl Inspect, for: Atom do
  require Macro

  def inspect(atom, opts) do
    color(Identifier.inspect_as_atom(atom), color_key(atom), opts)
  end

  defp color_key(atom) when is_boolean(atom), do: :boolean
  defp color_key(nil), do: nil
  defp color_key(_), do: :atom
end

defimpl Inspect, for: BitString do
  def inspect(term, opts) when is_binary(term) do
    %Inspect.Opts{binaries: bins, base: base, printable_limit: printable_limit} = opts

    if base == :decimal and
         (bins == :as_strings or (bins == :infer and String.printable?(term, printable_limit))) do
      inspected =
        case Identifier.escape(term, ?", printable_limit) do
          {escaped, ""} -> [?", escaped, ?"]
          {escaped, _} -> [?", escaped, ?", " <> ..."]
        end

      color(IO.iodata_to_binary(inspected), :string, opts)
    else
      inspect_bitstring(term, opts)
    end
  end

  def inspect(term, opts) do
    inspect_bitstring(term, opts)
  end

  defp inspect_bitstring("", opts) do
    color("<<>>", :binary, opts)
  end

  defp inspect_bitstring(bitstring, opts) do
    left = color("<<", :binary, opts)
    right = color(">>", :binary, opts)
    inner = each_bit(bitstring, opts.limit, opts)
    group(concat(concat(left, nest(inner, 2)), right))
  end

  defp each_bit(_, 0, _) do
    "..."
  end

  defp each_bit(<<>>, _counter, _opts) do
    :doc_nil
  end

  defp each_bit(<<h::8>>, _counter, opts) do
    Inspect.Integer.inspect(h, opts)
  end

  defp each_bit(<<h, t::bitstring>>, counter, opts) do
    flex_glue(
      concat(Inspect.Integer.inspect(h, opts), ","),
      each_bit(t, decrement(counter), opts)
    )
  end

  defp each_bit(bitstring, _counter, opts) do
    size = bit_size(bitstring)
    <<h::size(size)>> = bitstring
    Inspect.Integer.inspect(h, opts) <> "::size(" <> Integer.to_string(size) <> ")"
  end

  @compile {:inline, decrement: 1}
  defp decrement(:infinity), do: :infinity
  defp decrement(counter), do: counter - 1
end

defimpl Inspect, for: List do
  def inspect([], opts) do
    color("[]", :list, opts)
  end

  # TODO: Remove :char_list and :as_char_lists handling on v2.0
  def inspect(term, opts) do
    %Inspect.Opts{
      charlists: lists,
      char_lists: lists_deprecated,
      printable_limit: printable_limit
    } = opts

    lists =
      if lists == :infer and lists_deprecated != :infer do
        case lists_deprecated do
          :as_char_lists ->
            IO.warn(
              "the :char_lists inspect option and its :as_char_lists " <>
                "value are deprecated, use the :charlists option and its " <>
                ":as_charlists value instead"
            )

            :as_charlists

          _ ->
            IO.warn("the :char_lists inspect option is deprecated, use :charlists instead")
            lists_deprecated
        end
      else
        lists
      end

    open = color("[", :list, opts)
    sep = color(",", :list, opts)
    close = color("]", :list, opts)

    cond do
      lists == :as_charlists or (lists == :infer and List.ascii_printable?(term, printable_limit)) ->
        inspected =
          case Identifier.escape(IO.chardata_to_string(term), ?', printable_limit) do
            {escaped, ""} -> [?', escaped, ?']
            {escaped, _} -> [?', escaped, ?', " ++ ..."]
          end

        IO.iodata_to_binary(inspected)

      keyword?(term) ->
        container_doc(open, term, close, opts, &keyword/2, separator: sep, break: :strict)

      true ->
        container_doc(open, term, close, opts, &to_doc/2, separator: sep)
    end
  end

  @doc false
  def keyword({key, value}, opts) do
    key = color(Identifier.inspect_as_key(key), :atom, opts)
    concat(key, concat(" ", to_doc(value, opts)))
  end

  @doc false
  def keyword?([{key, _value} | rest]) when is_atom(key) do
    case Atom.to_charlist(key) do
      'Elixir.' ++ _ -> false
      _ -> keyword?(rest)
    end
  end

  def keyword?([]), do: true
  def keyword?(_other), do: false
end

defimpl Inspect, for: Tuple do
  def inspect(tuple, opts) do
    open = color("{", :tuple, opts)
    sep = color(",", :tuple, opts)
    close = color("}", :tuple, opts)
    container_opts = [separator: sep, break: :flex]
    container_doc(open, Tuple.to_list(tuple), close, opts, &to_doc/2, container_opts)
  end
end

defimpl Inspect, for: Map do
  def inspect(map, opts) do
    inspect(map, "", opts)
  end

  def inspect(map, name, opts) do
    map = :maps.to_list(map)
    open = color("%" <> name <> "{", :map, opts)
    sep = color(",", :map, opts)
    close = color("}", :map, opts)
    container_doc(open, map, close, opts, traverse_fun(map, opts), separator: sep, break: :strict)
  end

  defp traverse_fun(list, opts) do
    if Inspect.List.keyword?(list) do
      &Inspect.List.keyword/2
    else
      sep = color(" => ", :map, opts)
      &to_map(&1, &2, sep)
    end
  end

  defp to_map({key, value}, opts, sep) do
    concat(concat(to_doc(key, opts), sep), to_doc(value, opts))
  end
end

defimpl Inspect, for: Integer do
  def inspect(term, %Inspect.Opts{base: base} = opts) do
    inspected = Integer.to_string(term, base_to_value(base)) |> prepend_prefix(base)
    color(inspected, :number, opts)
  end

  defp base_to_value(base) do
    case base do
      :binary -> 2
      :decimal -> 10
      :octal -> 8
      :hex -> 16
    end
  end

  defp prepend_prefix(value, :decimal), do: value

  defp prepend_prefix(<<?-, value::binary>>, base) do
    "-" <> prepend_prefix(value, base)
  end

  defp prepend_prefix(value, base) do
    prefix =
      case base do
        :binary -> "0b"
        :octal -> "0o"
        :hex -> "0x"
      end

    prefix <> value
  end
end

defimpl Inspect, for: Float do
  def inspect(term, opts) do
    inspected = IO.iodata_to_binary(:io_lib_format.fwrite_g(term))
    color(inspected, :number, opts)
  end
end

defimpl Inspect, for: Regex do
  def inspect(regex, opts) do
    {escaped, _} = Identifier.escape(regex.source, ?/, :infinity, &escape_map/1)
    source = IO.iodata_to_binary(['~r/', escaped, ?/, regex.opts])
    color(source, :regex, opts)
  end

  defp escape_map(?\a), do: '\\a'
  defp escape_map(?\f), do: '\\f'
  defp escape_map(?\n), do: '\\n'
  defp escape_map(?\r), do: '\\r'
  defp escape_map(?\t), do: '\\t'
  defp escape_map(?\v), do: '\\v'
  defp escape_map(_), do: false
end

defimpl Inspect, for: Function do
  def inspect(function, _opts) do
    fun_info = Function.info(function)
    mod = fun_info[:module]
    name = fun_info[:name]

    cond do
      fun_info[:type] == :external and fun_info[:env] == [] ->
        inspected_as_atom = Identifier.inspect_as_atom(mod)
        inspected_as_function = Identifier.inspect_as_function(name)
        "&#{inspected_as_atom}.#{inspected_as_function}/#{fun_info[:arity]}"

      match?('elixir_compiler_' ++ _, Atom.to_charlist(mod)) ->
        if function_exported?(mod, :__RELATIVE__, 0) do
          "#Function<#{uniq(fun_info)} in file:#{mod.__RELATIVE__}>"
        else
          default_inspect(mod, fun_info)
        end

      true ->
        default_inspect(mod, fun_info)
    end
  end

  defp default_inspect(mod, fun_info) do
    inspected_as_atom = Identifier.inspect_as_atom(mod)
    extracted_name = extract_name(fun_info[:name])
    "#Function<#{uniq(fun_info)}/#{fun_info[:arity]} in #{inspected_as_atom}#{extracted_name}>"
  end

  defp extract_name([]) do
    ""
  end

  defp extract_name(name) do
    case Identifier.extract_anonymous_fun_parent(name) do
      {name, arity} ->
        "." <> Identifier.inspect_as_function(name) <> "/" <> arity

      :error ->
        "." <> Identifier.inspect_as_function(name)
    end
  end

  defp uniq(fun_info) do
    Integer.to_string(fun_info[:new_index]) <> "." <> Integer.to_string(fun_info[:uniq])
  end
end

defimpl Inspect, for: PID do
  def inspect(pid, _opts) do
    "#PID" <> IO.iodata_to_binary(:erlang.pid_to_list(pid))
  end
end

defimpl Inspect, for: Port do
  def inspect(port, _opts) do
    IO.iodata_to_binary(:erlang.port_to_list(port))
  end
end

defimpl Inspect, for: Reference do
  def inspect(ref, _opts) do
    '#Ref' ++ rest = :erlang.ref_to_list(ref)
    "#Reference" <> IO.iodata_to_binary(rest)
  end
end

defimpl Inspect, for: Any do
  defmacro __deriving__(module, struct, options) do
    fields =
      struct
      |> Map.drop([:__exception__, :__struct__])
      |> Map.keys()

    only = Keyword.get(options, :only, fields)
    except = Keyword.get(options, :except, [])

    filtered_fields =
      fields
      |> Enum.reject(&(&1 in except))
      |> Enum.filter(&(&1 in only))

    inspect_module =
      if fields == only and except == [] do
        quote(do: Inspect.Map)
      else
        quote(do: Inspect.Any)
      end

    quote do
      defimpl Inspect, for: unquote(module) do
        def inspect(struct, opts) do
          map = Map.take(struct, unquote(filtered_fields))
          name = Identifier.inspect_as_atom(unquote(module))
          unquote(inspect_module).inspect(map, name, opts)
        end
      end
    end
  end

  def inspect(%module{} = struct, opts) do
    try do
      module.__struct__
    rescue
      _ -> Inspect.Map.inspect(struct, opts)
    else
      dunder ->
        if :maps.keys(dunder) == :maps.keys(struct) do
          pruned = :maps.remove(:__exception__, :maps.remove(:__struct__, struct))
          Inspect.Map.inspect(pruned, Identifier.inspect_as_atom(module), opts)
        else
          Inspect.Map.inspect(struct, opts)
        end
    end
  end

  def inspect(map, name, opts) do
    # Use the :limit option and an extra element to force
    # `container_doc/6` to append "...".
    opts = %{opts | limit: min(opts.limit, map_size(map))}
    map = :maps.to_list(map) ++ ["..."]

    open = color("#" <> name <> "<", :map, opts)
    sep = color(",", :map, opts)
    close = color(">", :map, opts)

    container_doc(open, map, close, opts, &Inspect.List.keyword/2, separator: sep, break: :strict)
  end
end

import Kernel, except: [inspect: 1]
import Inspect.Algebra

alias Code.Identifier

defprotocol Inspect do
  @moduledoc """
  The `Inspect` protocol converts an Elixir data structure into an
  algebra document.

  This documentation refers to implementing the `Inspect` protocol
  for your own data structures. To learn more about using inspect,
  see `Kernel.inspect/2` and `IO.inspect/2`.

  The `inspect/2` function receives the entity to be inspected
  followed by the inspecting options, represented by the struct
  `Inspect.Opts`. Building of the algebra document is done with
  `Inspect.Algebra`.

  ## Examples

  Many times, inspecting a structure can be implemented in function
  of existing entities. For example, here is `MapSet`'s `inspect/2`
  implementation:

      defimpl Inspect, for: MapSet do
        import Inspect.Algebra

        def inspect(dict, opts) do
          concat(["#MapSet<", to_doc(MapSet.to_list(dict), opts), ">"])
        end
      end

  The `concat/1` function comes from `Inspect.Algebra` and it
  concatenates algebra documents together. In the example above,
  it is concatenating the string `"MapSet<"` (all strings are
  valid algebra documents that keep their formatting when pretty
  printed), the document returned by `Inspect.Algebra.to_doc/2` and the
  other string `">"`.

  Since regular strings are valid entities in an algebra document,
  an implementation of the `Inspect` protocol may simply return a
  string, although that will devoid it of any pretty-printing.

  ## Error handling

  In case there is an error while your structure is being inspected,
  Elixir will raise an `ArgumentError` error and will automatically fall back
  to a raw representation for printing the structure.

  You can however access the underlying error by invoking the `Inspect`
  implementation directly. For example, to test `Inspect.MapSet` above,
  you can invoke it as:

      Inspect.MapSet.inspect(MapSet.new(), %Inspect.Opts{})

  ## Deriving

  The `Inspect` protocol can be derived to hide certain fields from
  structs, so they don't show up in logs, inspects and similar. This
  is especially useful for fields containing private information.

  The options `:only` and `:except` can be used with `@derive` to
  specify which fields should and should not appear in the
  algebra document:

      defmodule User do
        @derive {Inspect, only: [:id, :name]}
        defstruct [:id, :name, :address]
      end

      inspect(%User{id: 1, name: "Homer", address: "742 Evergreen Terrace"})
      #=> #User<id: 1, name: "Homer", ...>

  """

  # Handle structs in Any
  @fallback_to_any true

  def inspect(term, opts)
end

defimpl Inspect, for: Atom do
  require Macro

  def inspect(atom, opts) do
    color(Identifier.inspect_as_atom(atom), color_key(atom), opts)
  end

  defp color_key(atom) when is_boolean(atom), do: :boolean
  defp color_key(nil), do: nil
  defp color_key(_), do: :atom
end

defimpl Inspect, for: BitString do
  def inspect(term, opts) when is_binary(term) do
    %Inspect.Opts{binaries: bins, base: base, printable_limit: printable_limit} = opts

    if base == :decimal and
         (bins == :as_strings or (bins == :infer and String.printable?(term, printable_limit))) do
      inspected =
        case Identifier.escape(term, ?", printable_limit) do
          {escaped, ""} -> [?", escaped, ?"]
          {escaped, _} -> [?", escaped, ?", " <> ..."]
        end

      color(IO.iodata_to_binary(inspected), :string, opts)
    else
      inspect_bitstring(term, opts)
    end
  end

  def inspect(term, opts) do
    inspect_bitstring(term, opts)
  end

  defp inspect_bitstring("", opts) do
    color("<<>>", :binary, opts)
  end

  defp inspect_bitstring(bitstring, opts) do
    left = color("<<", :binary, opts)
    right = color(">>", :binary, opts)
    inner = each_bit(bitstring, opts.limit, opts)
    group(concat(concat(left, nest(inner, 2)), right))
  end

  defp each_bit(_, 0, _) do
    "..."
  end

  defp each_bit(<<>>, _counter, _opts) do
    :doc_nil
  end

  defp each_bit(<<h::8>>, _counter, opts) do
    Inspect.Integer.inspect(h, opts)
  end

  defp each_bit(<<h, t::bitstring>>, counter, opts) do
    flex_glue(
      concat(Inspect.Integer.inspect(h, opts), ","),
      each_bit(t, decrement(counter), opts)
    )
  end

  defp each_bit(bitstring, _counter, opts) do
    size = bit_size(bitstring)
    <<h::size(size)>> = bitstring
    Inspect.Integer.inspect(h, opts) <> "::size(" <> Integer.to_string(size) <> ")"
  end

  @compile {:inline, decrement: 1}
  defp decrement(:infinity), do: :infinity
  defp decrement(counter), do: counter - 1
end

defimpl Inspect, for: List do
  def inspect([], opts) do
    color("[]", :list, opts)
  end

  # TODO: Remove :char_list and :as_char_lists handling on v2.0
  def inspect(term, opts) do
    %Inspect.Opts{
      charlists: lists,
      char_lists: lists_deprecated,
      printable_limit: printable_limit
    } = opts

    lists =
      if lists == :infer and lists_deprecated != :infer do
        case lists_deprecated do
          :as_char_lists ->
            IO.warn(
              "the :char_lists inspect option and its :as_char_lists " <>
                "value are deprecated, use the :charlists option and its " <>
                ":as_charlists value instead"
            )

            :as_charlists

          _ ->
            IO.warn("the :char_lists inspect option is deprecated, use :charlists instead")
            lists_deprecated
        end
      else
        lists
      end

    open = color("[", :list, opts)
    sep = color(",", :list, opts)
    close = color("]", :list, opts)

    cond do
      lists == :as_charlists or (lists == :infer and List.ascii_printable?(term, printable_limit)) ->
        inspected =
          case Identifier.escape(IO.chardata_to_string(term), ?', printable_limit) do
            {escaped, ""} -> [?', escaped, ?']
            {escaped, _} -> [?', escaped, ?', " ++ ..."]
          end

        IO.iodata_to_binary(inspected)

      keyword?(term) ->
        container_doc(open, term, close, opts, &keyword/2, separator: sep, break: :strict)

      true ->
        container_doc(open, term, close, opts, &to_doc/2, separator: sep)
    end
  end

  @doc false
  def keyword({key, value}, opts) do
    key = color(Identifier.inspect_as_key(key), :atom, opts)
    concat(key, concat(" ", to_doc(value, opts)))
  end

  @doc false
  def keyword?([{key, _value} | rest]) when is_atom(key) do
    case Atom.to_charlist(key) do
      'Elixir.' ++ _ -> false
      _ -> keyword?(rest)
    end
  end

  def keyword?([]), do: true
  def keyword?(_other), do: false
end

defimpl Inspect, for: Tuple do
  def inspect(tuple, opts) do
    open = color("{", :tuple, opts)
    sep = color(",", :tuple, opts)
    close = color("}", :tuple, opts)
    container_opts = [separator: sep, break: :flex]
    container_doc(open, Tuple.to_list(tuple), close, opts, &to_doc/2, container_opts)
  end
end

defimpl Inspect, for: Map do
  def inspect(map, opts) do
    inspect(map, "", opts)
  end

  def inspect(map, name, opts) do
    map = :maps.to_list(map)
    open = color("%" <> name <> "{", :map, opts)
    sep = color(",", :map, opts)
    close = color("}", :map, opts)
    container_doc(open, map, close, opts, traverse_fun(map, opts), separator: sep, break: :strict)
  end

  defp traverse_fun(list, opts) do
    if Inspect.List.keyword?(list) do
      &Inspect.List.keyword/2
    else
      sep = color(" => ", :map, opts)
      &to_map(&1, &2, sep)
    end
  end

  defp to_map({key, value}, opts, sep) do
    concat(concat(to_doc(key, opts), sep), to_doc(value, opts))
  end
end

defimpl Inspect, for: Integer do
  def inspect(term, %Inspect.Opts{base: base} = opts) do
    inspected = Integer.to_string(term, base_to_value(base)) |> prepend_prefix(base)
    color(inspected, :number, opts)
  end

  defp base_to_value(base) do
    case base do
      :binary -> 2
      :decimal -> 10
      :octal -> 8
      :hex -> 16
    end
  end

  defp prepend_prefix(value, :decimal), do: value

  defp prepend_prefix(<<?-, value::binary>>, base) do
    "-" <> prepend_prefix(value, base)
  end

  defp prepend_prefix(value, base) do
    prefix =
      case base do
        :binary -> "0b"
        :octal -> "0o"
        :hex -> "0x"
      end

    prefix <> value
  end
end

defimpl Inspect, for: Float do
  def inspect(term, opts) do
    inspected = IO.iodata_to_binary(:io_lib_format.fwrite_g(term))
    color(inspected, :number, opts)
  end
end

defimpl Inspect, for: Regex do
  def inspect(regex, opts) do
    {escaped, _} = Identifier.escape(regex.source, ?/, :infinity, &escape_map/1)
    source = IO.iodata_to_binary(['~r/', escaped, ?/, regex.opts])
    color(source, :regex, opts)
  end

  defp escape_map(?\a), do: '\\a'
  defp escape_map(?\f), do: '\\f'
  defp escape_map(?\n), do: '\\n'
  defp escape_map(?\r), do: '\\r'
  defp escape_map(?\t), do: '\\t'
  defp escape_map(?\v), do: '\\v'
  defp escape_map(_), do: false
end

defimpl Inspect, for: Function do
  def inspect(function, _opts) do
    fun_info = Function.info(function)
    mod = fun_info[:module]
    name = fun_info[:name]

    cond do
      fun_info[:type] == :external and fun_info[:env] == [] ->
        inspected_as_atom = Identifier.inspect_as_atom(mod)
        inspected_as_function = Identifier.inspect_as_function(name)
        "&#{inspected_as_atom}.#{inspected_as_function}/#{fun_info[:arity]}"

      match?('elixir_compiler_' ++ _, Atom.to_charlist(mod)) ->
        if function_exported?(mod, :__RELATIVE__, 0) do
          "#Function<#{uniq(fun_info)} in file:#{mod.__RELATIVE__}>"
        else
          default_inspect(mod, fun_info)
        end

      true ->
        default_inspect(mod, fun_info)
    end
  end

  defp default_inspect(mod, fun_info) do
    inspected_as_atom = Identifier.inspect_as_atom(mod)
    extracted_name = extract_name(fun_info[:name])
    "#Function<#{uniq(fun_info)}/#{fun_info[:arity]} in #{inspected_as_atom}#{extracted_name}>"
  end

  defp extract_name([]) do
    ""
  end

  defp extract_name(name) do
    case Identifier.extract_anonymous_fun_parent(name) do
      {name, arity} ->
        "." <> Identifier.inspect_as_function(name) <> "/" <> arity

      :error ->
        "." <> Identifier.inspect_as_function(name)
    end
  end

  defp uniq(fun_info) do
    Integer.to_string(fun_info[:new_index]) <> "." <> Integer.to_string(fun_info[:uniq])
  end
end

defimpl Inspect, for: PID do
  def inspect(pid, _opts) do
    "#PID" <> IO.iodata_to_binary(:erlang.pid_to_list(pid))
  end
end

defimpl Inspect, for: Port do
  def inspect(port, _opts) do
    IO.iodata_to_binary(:erlang.port_to_list(port))
  end
end

defimpl Inspect, for: Reference do
  def inspect(ref, _opts) do
    '#Ref' ++ rest = :erlang.ref_to_list(ref)
    "#Reference" <> IO.iodata_to_binary(rest)
  end
end

defimpl Inspect, for: Any do
  defmacro __deriving__(module, struct, options) do
    fields =
      struct
      |> Map.drop([:__exception__, :__struct__])
      |> Map.keys()

    only = Keyword.get(options, :only, fields)
    except = Keyword.get(options, :except, [])

    filtered_fields =
      fields
      |> Enum.reject(&(&1 in except))
      |> Enum.filter(&(&1 in only))

    inspect_module =
      if fields == only and except == [] do
        quote(do: Inspect.Map)
      else
        quote(do: Inspect.Any)
      end

    quote do
      defimpl Inspect, for: unquote(module) do
        def inspect(struct, opts) do
          map = Map.take(struct, unquote(filtered_fields))
          colorless_opts = %{opts | syntax_colors: []}
          name = Inspect.Atom.inspect(unquote(module), colorless_opts)
          unquote(inspect_module).inspect(map, name, opts)
        end
      end
    end
  end

  def inspect(%module{} = struct, opts) do
    try do
      module.__struct__
    rescue
      _ -> Inspect.Map.inspect(struct, opts)
    else
      dunder ->
        if :maps.keys(dunder) == :maps.keys(struct) do
          pruned = :maps.remove(:__exception__, :maps.remove(:__struct__, struct))
          colorless_opts = %{opts | syntax_colors: []}
          Inspect.Map.inspect(pruned, Inspect.Atom.inspect(module, colorless_opts), opts)
        else
          Inspect.Map.inspect(struct, opts)
        end
    end
  end

  def inspect(map, name, opts) do
    # Use the :limit option and an extra element to force
    # `container_doc/6` to append "...".
    opts = %{opts | limit: min(opts.limit, map_size(map))}
    map = :maps.to_list(map) ++ ["..."]

    open = color("#" <> name <> "<", :map, opts)
    sep = color(",", :map, opts)
    close = color(">", :map, opts)

    container_doc(open, map, close, opts, &Inspect.List.keyword/2, separator: sep, break: :strict)
  end
end


Pass env and target to Mix.Config readers

defmodule Mix.Config do
  # TODO: Convert them to hard deprecations on v1.13

  @moduledoc deprecated: "Use Config and Config.Reader instead"
  @moduledoc ~S"""
  A simple configuration API and functions for managing config files.

  This module is deprecated, use the modules `Config` and `Config.Reader`
  from Elixir's standard library instead.

  ## Setting configuration

  Most commonly, this module is used to define your own configuration:

      use Mix.Config

      config :root_key,
        key1: "value1",
        key2: "value2"

      import_config "#{Mix.env()}.exs"

  `use Mix.Config` will import the functions `config/2`, `config/3`
  and `import_config/1` to help you manage your configuration.

  ## Evaluating configuration

  Once a configuration is written to a file, the functions in this
  module can be used to read and merge said configuration. The `eval!/2`
  function allows you to evaluate a given configuration file and the `merge/2`
  function allows you to deep merge the results of multiple configurations. Those
  functions should not be invoked by users writing configurations but
  rather by library authors.

  ## Examples

  The most common use of `Mix.Config` is to define application
  configuration so that `Application.get_env/3` and other `Application`
  functions can be used to retrieve or further change them.

  Application config files are typically placed in the `config/`
  directory of your Mix projects. For example, the following config

      # config/config.exs
      config :my_app, :key, "value"

  will be automatically loaded by Mix and persisted into the
  `:my_app`'s application environment, which can be accessed in
  its source code as follows:

      "value" = Application.fetch_env!(:my_app, :key1)

  """

  @doc false
  defmacro __using__(_) do
    quote do
      import Mix.Config, only: [config: 2, config: 3, import_config: 1]
    end
  end

  @doc """
  Configures the given `root_key`.

  Keyword lists are always deep merged.

  ## Examples

  The given `opts` are merged into the existing configuration
  for the given `root_key`. Conflicting keys are overridden by the
  ones specified in `opts`. For example, the application
  configuration below

      config :logger,
        level: :warn,
        backends: [:console]

      config :logger,
        level: :info,
        truncate: 1024

  will have a final configuration for `:logger` of:

      [level: :info, backends: [:console], truncate: 1024]

  """
  @doc deprecated: "Use the Config module instead"
  defdelegate config(root_key, opts), to: Config

  @doc """
  Configures the given `key` for the given `root_key`.

  Keyword lists are always deep merged.

  ## Examples

  The given `opts` are merged into the existing values for `key`
  in the given `root_key`. Conflicting keys are overridden by the
  ones specified in `opts`. For example, the application
  configuration below

      config :ecto, Repo,
        log_level: :warn,
        adapter: Ecto.Adapters.Postgres

      config :ecto, Repo,
        log_level: :info,
        pool_size: 10

  will have a final value of the configuration for the `Repo`
  key in the `:ecto` application of:

      [log_level: :info, pool_size: 10, adapter: Ecto.Adapters.Postgres]

  """
  @doc deprecated: "Use the Config module instead"
  defdelegate config(root_key, key, opts), to: Config

  @doc ~S"""
  Imports configuration from the given file or files.

  If `path_or_wildcard` is a wildcard, then all the files
  matching that wildcard will be imported; if no file matches
  the wildcard, no errors are raised. If `path_or_wildcard` is
  not a wildcard but a path to a single file, then that file is
  imported; in case the file doesn't exist, an error is raised.

  If path/wildcard is a relative path/wildcard, it will be expanded
  relatively to the directory the current configuration file is in.

  ## Examples

  This is often used to emulate configuration across environments:

      import_config "#{Mix.env()}.exs"

  Or to import files from children in umbrella projects:

      import_config "../apps/*/config/config.exs"

  """
  @doc deprecated: "Use the Config module instead"
  defmacro import_config(path_or_wildcard) do
    quote do
      Mix.Config.__import__!(unquote(path_or_wildcard), __DIR__)
    end
  end

  @doc false
  def __import__!(path_or_wildcard, dir) do
    path_or_wildcard = Path.expand(path_or_wildcard, dir)

    paths =
      if String.contains?(path_or_wildcard, ~w(* ? [ {)) do
        Path.wildcard(path_or_wildcard)
      else
        [path_or_wildcard]
      end

    for path <- paths do
      Config.__import__!(path)
    end

    :ok
  end

  ## Mix API

  @doc """
  Evaluates the given configuration file.

  It accepts a list of `imported_paths` that should raise if attempted
  to be imported again (to avoid recursive imports).

  It returns a tuple with the configuration and the imported paths.
  """
  @doc deprecated: "Use Config.Reader.read_imports!/2 instead"
  def eval!(file, imported_paths \\ []) do
    Config.Reader.read_imports!(file, imports: imported_paths, env: Mix.env(), target: Mix.target())
  end

  @doc """
  Reads the configuration file.

  The same as `eval!/2` but only returns the configuration
  in the given file, without returning the imported paths.

  It exists for convenience purposes. For example, you could
  invoke it inside your `mix.exs` to read some external data
  you decided to move to a configuration file:

      subsystem: Mix.Config.read!("rel/subsystem.exs")

  """
  @doc deprecated: "Use Config.Reader.read!/2 instead"
  @spec read!(Path.t(), [Path.t()]) :: keyword
  def read!(file, imported_paths \\ []) do
    Config.Reader.read!(file, imports: imported_paths, env: Mix.env(), target: Mix.target())
  end

  @doc """
  Merges two configurations.

  The configurations are merged together with the values in
  the second one having higher preference than the first in
  case of conflicts. In case both values are set to keyword
  lists, it deep merges them.

  ## Examples

      iex> Mix.Config.merge([app: [k: :v1]], [app: [k: :v2]])
      [app: [k: :v2]]

      iex> Mix.Config.merge([app: [k: [v1: 1, v2: 2]]], [app: [k: [v2: :a, v3: :b]]])
      [app: [k: [v1: 1, v2: :a, v3: :b]]]

      iex> Mix.Config.merge([app1: []], [app2: []])
      [app1: [], app2: []]

  """
  @doc deprecated: "Use Config.Reader.merge/2 instead"
  def merge(config1, config2) do
    Config.__merge__(config1, config2)
  end

  @doc """
  Persists the given configuration by modifying
  the configured applications environment.

  `config` should be a list of `{app, app_config}` tuples or a
  `%{app => app_config}` map where `app` are the applications to
  be configured and `app_config` are the configuration (as key-value
  pairs) for each of those applications.

  Returns the configured applications.

  ## Examples

      Mix.Config.persist(logger: [level: :error], my_app: [my_config: 1])
      #=> [:logger, :my_app]

  """
  @doc deprecated: "Use Application.put_all_env/2 instead"
  def persist(config) do
    Application.put_all_env(config, persistent: true)
  end

  @doc false
  @deprecated "Use eval!/2 instead"
  def read_wildcard!(path, loaded_paths \\ []) do
    paths =
      if String.contains?(path, ~w(* ? [ {)) do
        Path.wildcard(path)
      else
        [path]
      end

    Enum.reduce(paths, [], &merge(&2, read!(&1, loaded_paths)))
  end

  @doc false
  @deprecated "Manually validate the data instead"
  def validate!(config) do
    validate!(config, "runtime")
  end

  defp validate!(config, file) do
    if is_list(config) do
      Enum.all?(config, fn
        {app, value} when is_atom(app) ->
          if Keyword.keyword?(value) do
            true
          else
            raise ArgumentError,
                  "expected #{Path.relative_to_cwd(file)} config for app #{inspect(app)} " <>
                    "to return keyword list, got: #{inspect(value)}"
          end

        _ ->
          false
      end)
    else
      raise ArgumentError,
            "expected #{Path.relative_to_cwd(file)} config to return " <>
              "keyword list, got: #{inspect(config)}"
    end

    config
  end
end

defmodule Mix.Config do
  # TODO: Convert them to hard deprecations on v1.13

  @moduledoc deprecated: "Use Config and Config.Reader instead"
  @moduledoc ~S"""
  A simple configuration API and functions for managing config files.

  This module is deprecated, use the modules `Config` and `Config.Reader`
  from Elixir's standard library instead.

  ## Setting configuration

  Most commonly, this module is used to define your own configuration:

      use Mix.Config

      config :root_key,
        key1: "value1",
        key2: "value2"

      import_config "#{Mix.env()}.exs"

  `use Mix.Config` will import the functions `config/2`, `config/3`
  and `import_config/1` to help you manage your configuration.

  ## Evaluating configuration

  Once a configuration is written to a file, the functions in this
  module can be used to read and merge said configuration. The `eval!/2`
  function allows you to evaluate a given configuration file and the `merge/2`
  function allows you to deep merge the results of multiple configurations. Those
  functions should not be invoked by users writing configurations but
  rather by library authors.

  ## Examples

  The most common use of `Mix.Config` is to define application
  configuration so that `Application.get_env/3` and other `Application`
  functions can be used to retrieve or further change them.

  Application config files are typically placed in the `config/`
  directory of your Mix projects. For example, the following config

      # config/config.exs
      config :my_app, :key, "value"

  will be automatically loaded by Mix and persisted into the
  `:my_app`'s application environment, which can be accessed in
  its source code as follows:

      "value" = Application.fetch_env!(:my_app, :key1)

  """

  @doc false
  defmacro __using__(_) do
    quote do
      import Mix.Config, only: [config: 2, config: 3, import_config: 1]
    end
  end

  @doc """
  Configures the given `root_key`.

  Keyword lists are always deep merged.

  ## Examples

  The given `opts` are merged into the existing configuration
  for the given `root_key`. Conflicting keys are overridden by the
  ones specified in `opts`. For example, the application
  configuration below

      config :logger,
        level: :warn,
        backends: [:console]

      config :logger,
        level: :info,
        truncate: 1024

  will have a final configuration for `:logger` of:

      [level: :info, backends: [:console], truncate: 1024]

  """
  @doc deprecated: "Use the Config module instead"
  defdelegate config(root_key, opts), to: Config

  @doc """
  Configures the given `key` for the given `root_key`.

  Keyword lists are always deep merged.

  ## Examples

  The given `opts` are merged into the existing values for `key`
  in the given `root_key`. Conflicting keys are overridden by the
  ones specified in `opts`. For example, the application
  configuration below

      config :ecto, Repo,
        log_level: :warn,
        adapter: Ecto.Adapters.Postgres

      config :ecto, Repo,
        log_level: :info,
        pool_size: 10

  will have a final value of the configuration for the `Repo`
  key in the `:ecto` application of:

      [log_level: :info, pool_size: 10, adapter: Ecto.Adapters.Postgres]

  """
  @doc deprecated: "Use the Config module instead"
  defdelegate config(root_key, key, opts), to: Config

  @doc ~S"""
  Imports configuration from the given file or files.

  If `path_or_wildcard` is a wildcard, then all the files
  matching that wildcard will be imported; if no file matches
  the wildcard, no errors are raised. If `path_or_wildcard` is
  not a wildcard but a path to a single file, then that file is
  imported; in case the file doesn't exist, an error is raised.

  If path/wildcard is a relative path/wildcard, it will be expanded
  relatively to the directory the current configuration file is in.

  ## Examples

  This is often used to emulate configuration across environments:

      import_config "#{Mix.env()}.exs"

  Or to import files from children in umbrella projects:

      import_config "../apps/*/config/config.exs"

  """
  @doc deprecated: "Use the Config module instead"
  defmacro import_config(path_or_wildcard) do
    quote do
      Mix.Config.__import__!(unquote(path_or_wildcard), __DIR__)
    end
  end

  @doc false
  def __import__!(path_or_wildcard, dir) do
    path_or_wildcard = Path.expand(path_or_wildcard, dir)

    paths =
      if String.contains?(path_or_wildcard, ~w(* ? [ {)) do
        Path.wildcard(path_or_wildcard)
      else
        [path_or_wildcard]
      end

    for path <- paths do
      Config.__import__!(path)
    end

    :ok
  end

  ## Mix API

  @doc """
  Evaluates the given configuration file.

  It accepts a list of `imported_paths` that should raise if attempted
  to be imported again (to avoid recursive imports).

  It returns a tuple with the configuration and the imported paths.
  """
  @doc deprecated: "Use Config.Reader.read_imports!/2 instead"
  def eval!(file, imported_paths \\ []) do
    Config.Reader.read_imports!(file, imports: imported_paths)
  end

  @doc """
  Reads the configuration file.

  The same as `eval!/2` but only returns the configuration
  in the given file, without returning the imported paths.

  It exists for convenience purposes. For example, you could
  invoke it inside your `mix.exs` to read some external data
  you decided to move to a configuration file:

      subsystem: Mix.Config.read!("rel/subsystem.exs")

  """
  @doc deprecated: "Use Config.Reader.read!/2 instead"
  @spec read!(Path.t(), [Path.t()]) :: keyword
  def read!(file, imported_paths \\ []) do
    Config.Reader.read!(file, imports: imported_paths)
  end

  @doc """
  Merges two configurations.

  The configurations are merged together with the values in
  the second one having higher preference than the first in
  case of conflicts. In case both values are set to keyword
  lists, it deep merges them.

  ## Examples

      iex> Mix.Config.merge([app: [k: :v1]], [app: [k: :v2]])
      [app: [k: :v2]]

      iex> Mix.Config.merge([app: [k: [v1: 1, v2: 2]]], [app: [k: [v2: :a, v3: :b]]])
      [app: [k: [v1: 1, v2: :a, v3: :b]]]

      iex> Mix.Config.merge([app1: []], [app2: []])
      [app1: [], app2: []]

  """
  @doc deprecated: "Use Config.Reader.merge/2 instead"
  def merge(config1, config2) do
    Config.__merge__(config1, config2)
  end

  @doc """
  Persists the given configuration by modifying
  the configured applications environment.

  `config` should be a list of `{app, app_config}` tuples or a
  `%{app => app_config}` map where `app` are the applications to
  be configured and `app_config` are the configuration (as key-value
  pairs) for each of those applications.

  Returns the configured applications.

  ## Examples

      Mix.Config.persist(logger: [level: :error], my_app: [my_config: 1])
      #=> [:logger, :my_app]

  """
  @doc deprecated: "Use Application.put_all_env/2 instead"
  def persist(config) do
    Application.put_all_env(config, persistent: true)
  end

  @doc false
  @deprecated "Use eval!/2 instead"
  def read_wildcard!(path, loaded_paths \\ []) do
    paths =
      if String.contains?(path, ~w(* ? [ {)) do
        Path.wildcard(path)
      else
        [path]
      end

    Enum.reduce(paths, [], &merge(&2, read!(&1, loaded_paths)))
  end

  @doc false
  @deprecated "Manually validate the data instead"
  def validate!(config) do
    validate!(config, "runtime")
  end

  defp validate!(config, file) do
    if is_list(config) do
      Enum.all?(config, fn
        {app, value} when is_atom(app) ->
          if Keyword.keyword?(value) do
            true
          else
            raise ArgumentError,
                  "expected #{Path.relative_to_cwd(file)} config for app #{inspect(app)} " <>
                    "to return keyword list, got: #{inspect(value)}"
          end

        _ ->
          false
      end)
    else
      raise ArgumentError,
            "expected #{Path.relative_to_cwd(file)} config to return " <>
              "keyword list, got: #{inspect(config)}"
    end

    config
  end
end


remove puts

remove puts

defmodule Pan.Parser.Persona do
  use Pan.Web, :controller

  def get_or_insert(persona_map) do
    # The idea is to set the pid to be imported as strong as possible
    # That is panoptikum:pid > uri > email > name; but if the pid in
    # the database does not fit, we still fall back to weaker matches,
    # but not on the name, as names are no unique identifiers.

    persona_map = Map.put_new(persona_map, :pid,
                              UUID.uuid5(:url, persona_map[:uri] ||
                                               persona_map[:email] ||
                                               persona_map[:name]))

    persona_map =
      if persona_map[:email] do
        Map.put_new(persona_map, :name, persona_map[:email]
                                        |> String.split("@")
                                        |> List.first()
                                        |> String.split(".")
                                        |> Stream.map(&String.capitalize/1)
                                        |> Enum.join(" "))
      else
        persona_map
      end

    persona_map = Map.put_new(persona_map, :uri, persona_map[:email])
    email = persona_map[:email] || ""

    case Repo.get_by(PanWeb.Persona, pid:   persona_map[:pid]) ||
         Repo.get_by(PanWeb.Persona, pid:   persona_map[:uri] || "") ||
         Repo.get_by(PanWeb.Persona, uri:   persona_map[:uri] || "") ||
         Repo.one(from p in PanWeb.Persona, where: p.email == ^email,
                                            limit: 1) do
      nil ->
        %PanWeb.Persona{}
        |> Map.merge(persona_map)
        |> Repo.insert()
      persona ->
        {:ok, persona}
    end
  end
end

defmodule Pan.Parser.Persona do
  use Pan.Web, :controller

  def get_or_insert(persona_map) do
    # The idea is to set the pid to be imported as strong as possible
    # That is panoptikum:pid > uri > email > name; but if the pid in
    # the database does not fit, we still fall back to weaker matches,
    # but not on the name, as names are no unique identifiers.

    persona_map = Map.put_new(persona_map, :pid,
                              UUID.uuid5(:url, persona_map[:uri] ||
                                               persona_map[:email] ||
                                               persona_map[:name]))

    persona_map =
      if persona_map[:email] do
        Map.put_new(persona_map, :name, persona_map[:email]
                                        |> String.split("@")
                                        |> List.first()
                                        |> String.split(".")
                                        |> Stream.map(&String.capitalize/1)
                                        |> Enum.join(" "))
      else
        persona_map
      end

    persona_map = Map.put_new(persona_map, :uri, persona_map[:email])
    email = persona_map[:email] || ""

IO.inspect persona_map

    case Repo.get_by(PanWeb.Persona, pid:   persona_map[:pid]) ||
         Repo.get_by(PanWeb.Persona, pid:   persona_map[:uri] || "") ||
         Repo.get_by(PanWeb.Persona, uri:   persona_map[:uri] || "") ||
         Repo.one(from p in PanWeb.Persona, where: p.email == ^email,
                                            limit: 1) do
      nil ->
        %PanWeb.Persona{}
        |> Map.merge(persona_map)
        |> Repo.insert()
      persona ->
        {:ok, persona}
    end
  end
end