defmodule Ash.Query.Function.StringJoin do @moduledoc """ Joins a list of values. Ignores `nil` values and concatenates the remaining non-nil values. An optional joiner can be provided. string_join([first_name, last_name], " ") string_join([item_a, item_b]) """ use Ash.Query.Function, name: :string_join alias Ash.CiString def args, do: [ [{:array, :string}], [{:array, :string}, :string], [{:array, :string}, :ci_string], [{:array, :ci_string}], [{:array, :ci_string}, :ci_string], [{:array, :ci_string}, :ci_string] ] def evaluate(%{arguments: [values, joiner]}) do join(values, joiner) end def evaluate(%{arguments: [values]}) do join(values, "") end defp join(values, joiner) do joined = normalize_and_join(values, joiner) if has_ci_string?([joiner | values]) do {:known, CiString.new(joined)} else {:known, joined} end end defp normalize_and_join(values, joiner) do joiner = joiner |> normalize() values |> Enum.reject(&is_nil/1) |> Enum.map_join(joiner, &normalize/1) end defp normalize(%CiString{} = ci_string), do: ci_string |> CiString.value() defp normalize(string) when is_binary(string), do: string defp has_ci_string?(values) do values |> Enum.any?(fn %CiString{} -> true _ -> false end) end end