defmodule QuackDB.List do @moduledoc """ SQL expression helpers for DuckDB LIST/ARRAY values. These helpers return iodata and map directly to DuckDB list functions. They are intentionally small building blocks for larger query modules. """ @doc "Builds `len(list)`." @spec length(iodata()) :: iodata() def length(list_expression), do: call("len", [list_expression]) @doc "Builds `list_extract(list, index)`. DuckDB list indexes are 1-based." @spec extract(iodata(), iodata()) :: iodata() def extract(list_expression, index_expression) do call("list_extract", [list_expression, index_expression]) end @doc "Builds `list_slice(list, begin, end)`." @spec slice(iodata(), iodata(), iodata()) :: iodata() def slice(list_expression, begin_expression, end_expression) do call("list_slice", [list_expression, begin_expression, end_expression]) end @doc "Builds `list_slice(list, begin, end, step)`." @spec slice(iodata(), iodata(), iodata(), iodata()) :: iodata() def slice(list_expression, begin_expression, end_expression, step_expression) do call("list_slice", [list_expression, begin_expression, end_expression, step_expression]) end @doc "Builds `list_sort(list)`." @spec sort(iodata()) :: iodata() def sort(list_expression), do: call("list_sort", [list_expression]) @doc "Builds `list_reverse_sort(list)`." @spec reverse_sort(iodata()) :: iodata() def reverse_sort(list_expression), do: call("list_reverse_sort", [list_expression]) @doc "Builds `list_distinct(list)`." @spec distinct(iodata()) :: iodata() def distinct(list_expression), do: call("list_distinct", [list_expression]) @doc "Builds `list_unique(list)`." @spec unique(iodata()) :: iodata() def unique(list_expression), do: call("list_unique", [list_expression]) @doc "Builds `list_position(list, value)`." @spec position(iodata(), iodata()) :: iodata() def position(list_expression, value_expression) do call("list_position", [list_expression, value_expression]) end @doc "Builds `list_contains(list, value)`." @spec contains(iodata(), iodata()) :: iodata() def contains(list_expression, value_expression) do call("list_contains", [list_expression, value_expression]) end @doc "Builds `list_has_any(left, right)`." @spec has_any(iodata(), iodata()) :: iodata() def has_any(left_expression, right_expression) do call("list_has_any", [left_expression, right_expression]) end @doc "Builds `list_has_all(left, right)`." @spec has_all(iodata(), iodata()) :: iodata() def has_all(left_expression, right_expression) do call("list_has_all", [left_expression, right_expression]) end @doc "Builds `list_intersect(left, right)`." @spec intersect(iodata(), iodata()) :: iodata() def intersect(left_expression, right_expression) do call("list_intersect", [left_expression, right_expression]) end @doc "Builds `list_concat(left, right)`." @spec concat(iodata(), iodata()) :: iodata() def concat(left_expression, right_expression) do call("list_concat", [left_expression, right_expression]) end @doc "Builds `unnest(list)`." @spec unnest(iodata()) :: iodata() def unnest(list_expression), do: call("unnest", [list_expression]) defp call(function, args) do [function, "(", Enum.intersperse(args, ", "), ")"] end end