defmodule ExOpenAi.Parser do @moduledoc """ Parses the response from the OpenAI API. """ @type metadata :: map @type http_status_code :: number @type key :: String.t() @type success :: {:ok, map} @type success_delete :: :ok @type error :: {:error, map, http_status_code} @type parsed_response :: success | error @doc """ Parse a response expected to contain a resource. If you pass in a module as the first argument, the JSON will be parsed into that module's `__struct__`. ## Examples Given you have a module named `Resource`, defined like this: defmodule Resource do defstruct sid: nil end You can parse JSON into that module's struct like so: iex> response = %{body: "{ \\"id\\": \\"cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7\\" }", status_code: 200} ...> ExOpenAi.Parser.parse(response, Resource, nil) {:ok, %Resource{id: "cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7"}} You can also parse into a regular map if you want. iex> response = %{body: "{ \\"id\\": \\"cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7\\" }", status_code: 200} ...> ExOpenAi.Parser.parse(response, %{}, nil) {:ok, %{"id" => "cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7"}} """ @spec parse(HTTPoison.Response.t() | String.t(), module, boolean() | nil) :: success | error def parse(response, map, _simple) when is_map(map) and not :erlang.is_map_key(:__struct__, map) do handle_errors(response, fn body -> Jason.decode!(body) end) end def parse(response, module, simple) when is_binary(response) do handle_errors(response, fn body -> struct(module, Jason.decode!(body, keys: :atoms)) |> module.keep_it_simple(simple) end) end def parse(response, module, simple) do handle_errors(response, fn body -> struct(module, Jason.decode!(body, keys: :atoms)) |> module.keep_it_simple(simple) end) end defp handle_errors(response, fun) when is_binary(response), do: {:ok, fun.(response)} defp handle_errors(response, fun) do case response do %{body: body, status_code: status} when status in [200, 201] -> {:ok, fun.(body)} %{body: _, status_code: status} when status in [202, 204] -> :ok %{body: body, status_code: status} -> {:error, Jason.decode!(body), status} end end end