lib/ex_typesense/parser.ex

defmodule ExTypesense.Parser do
  @moduledoc since: "0.1.0"
  @moduledoc """
  Module for converting structs to raw json body or map.
  """

  @type collection() :: %{
          name: String.t(),
          fields: list(map()),
          default_sorting_field: String.t()
        }

  @doc since: "0.1.0"
  @spec struct_to_raw_body(module(), String.t()) :: String.t()
  def struct_to_raw_body(module_name, default_sorting_field \\ "") do
    module_name
    |> struct_to_map(default_sorting_field)
    |> Jason.encode!()
  end

  @doc """
  Converts an ecto schema module (e.g. `Posts`) to a map.

  ## Examples
      default_sorting_field = "title"
      iex> ExTypesense.Parser.struct_to_map(AppModule, default_sorting_field)
      %{
        default_sorting_field: "title",
        fields: [...],
        name: "companies"
      }
  """
  @doc since: "0.1.0"
  @spec struct_to_map(module(), String.t()) :: collection()
  def struct_to_map(module_name, default_sorting_field \\ "") do
    %{
      name: module_name.__schema__(:source),
      fields: get_fields(module_name),
      default_sorting_field: default_sorting_field
    }
  end

  @doc """
  Gets ecto schema fields and turns it into a list of maps as field entries.
  """
  @doc since: "0.1.0"
  @spec get_fields(module()) :: list(map())
  def get_fields(module_name) do
    :fields
    |> module_name.__schema__()
    |> Enum.filter(fn field_name ->
      field_name = to_string(field_name)

      cond do
        String.contains?(field_name, "id") -> false
        String.contains?(field_name, "_id") -> false
        String.contains?(field_name, "inserted_at") -> false
        String.contains?(field_name, "updated_at") -> false
        true -> true
      end
    end)
    |> Enum.map(fn field_name ->
      type =
        :type
        |> module_name.__schema__(field_name)
        |> to_string()

      Map.new([
        {:name, to_string(field_name)},
        {:type, type},
        {:sort, true},
        {:facet, true}
      ])
    end)
  end
end