defmodule Kagi.HTTP do
@moduledoc false
alias Kagi.Client
alias Kagi.Error
@type response :: %{status: pos_integer(), body: term()}
@spec get(Client.t(), String.t(), keyword()) :: {:ok, response()} | {:error, Error.t()}
def get(%Client{} = client, url, options) when is_binary(url) and is_list(options) do
request =
client.req_options
|> Keyword.put(:url, url)
|> Keyword.put(:method, :get)
|> Req.new()
|> Req.merge(options)
with {:ok, %Req.Response{status: status, body: body}} <-
request |> CloakedReq.attach() |> Req.request() |> normalize_request_error() do
handle_status(status, body)
end
end
@spec normalize_request_error({:ok, Req.Response.t()} | {:error, Exception.t()}) ::
{:ok, Req.Response.t()} | {:error, Error.t()}
defp normalize_request_error({:ok, %Req.Response{} = response}), do: {:ok, response}
defp normalize_request_error({:error, exception}) do
{:error, Error.new(:request_failed, Exception.message(exception))}
end
@spec handle_status(pos_integer(), term()) :: {:ok, response()} | {:error, Error.t()}
defp handle_status(status, _body) when status in [401, 403] do
{:error, Error.new(:unauthorized, "invalid or expired session token")}
end
defp handle_status(429, _body), do: {:error, Error.new(:rate_limited, "rate limited")}
defp handle_status(status, body) when status >= 200 and status <= 299 do
{:ok, %{status: status, body: body}}
end
defp handle_status(status, _body), do: {:error, Error.new(:http_error, "HTTP #{status}")}
end