lib/tesla/middleware/decode_rels.ex

defmodule Tesla.Middleware.DecodeRels do
  @moduledoc """
  Decode `Link` Hypermedia HTTP header into `opts[:rels]` field in response.

  ## Examples

  ```
  defmodule MyClient do
    use Tesla

    plug Tesla.Middleware.DecodeRels
  end

  env = MyClient.get("/...")

  env.opts[:rels]
  # => %{"Next" => "http://...", "Prev" => "..."}
  ```
  """

  @behaviour Tesla.Middleware

  @impl Tesla.Middleware
  def call(env, next, _opts) do
    env
    |> Tesla.run(next)
    |> parse_rels
  end

  defp parse_rels({:ok, env}), do: {:ok, parse_rels(env)}
  defp parse_rels({:error, reason}), do: {:error, reason}

  defp parse_rels(env) do
    if link = Tesla.get_header(env, "link") do
      Tesla.put_opt(env, :rels, rels(link))
    else
      env
    end
  end

  defp rels(link) do
    link
    |> String.split(",")
    |> Enum.map(&String.trim/1)
    |> Enum.map(&rel/1)
    |> Enum.into(%{})
  end

  defp rel(item) do
    Regex.run(~r/\A<(.+)>; rel=["]?([^"]+)["]?\z/, item, capture: :all_but_first)
    |> Enum.reverse()
    |> List.to_tuple()
  end
end