diff --git a/lib/lua/table.ex b/lib/lua/table.ex new file mode 100644 index 0000000..65f0485 --- /dev/null +++ b/lib/lua/table.ex @@ -0,0 +1,76 @@ +defmodule Lua.Table do + @moduledoc """ + In Lua, [tables](https://www.lua.org/pil/2.5.html) are the fundamental datastructure, + which are used both as associative arrays (maps), and arrays (lists). + + `Lua.Table` provides some utilities for working with Lua tables when passed back to + Elixir. + """ + + @doc """ + Converts a Lua table into a list. Assumes that the + table is correctly ordered. + + iex> Lua.Table.as_list([{1, "a"}, {2, "b"}, {3, "c"}]) + ["a", "b", "c"] + + To ensure the list is ordered, you can pass the `:sort` option + + iex> Lua.Table.as_list([{2, "b"}, {1, "a"}, {3, "c"}]) + ["b", "a", "c"] + + iex> Lua.Table.as_list([{2, "b"}, {1, "a"}, {3, "c"}], sort: true) + ["a", "b", "c"] + + """ + def as_list(values, opts \\ []) do + opts = Keyword.validate!(opts, sort: false) + + sorter = + if Keyword.fetch!(opts, :sort) do + &List.keysort(&1, 0) + else + &Function.identity/1 + end + + values + |> sorter.() + |> Enum.map(fn {_, v} -> v end) + end + + @doc """ + Converts a Lua table into a map + + iex> Lua.Table.as_map([{"a", 1}, {"b", 2}]) + %{"a" => 1, "b" => 2} + """ + def as_map(values) do + Map.new(values) + end + + @doc """ + Converts a Lua table into more "native" feeling lists and + maps, deeply traversing any sub-tables. + + It uses the heuristic that maps with integer keys starting + as 1 will be auto-cast into lists + + iex> Lua.Table.deep_cast([{"a", 1}, {"b", [{1, 3}, {2, 4}]}]) + %{"a" => 1, "b" => [3, 4]} + """ + def deep_cast(value) do + case value do + [{1, _val} | _rest] = list -> + Enum.map(list, fn + {_, v} when is_list(v) -> deep_cast(v) + {_, v} -> v + end) + + map -> + Map.new(map, fn + {k, v} when is_list(v) -> {k, deep_cast(v)} + {k, v} -> {k, v} + end) + end + end +end diff --git a/test/lua/api_test.exs b/test/lua/api_test.exs index 7547ec4..b58a1dc 100644 --- a/test/lua/api_test.exs +++ b/test/lua/api_test.exs @@ -1,5 +1,5 @@ defmodule Lua.APITest do - use ExUnit.Case + use ExUnit.Case, async: true alias Lua diff --git a/test/lua/table_test.exs b/test/lua/table_test.exs new file mode 100644 index 0000000..cc6f56d --- /dev/null +++ b/test/lua/table_test.exs @@ -0,0 +1,5 @@ +defmodule Lua.TableTest do + use ExUnit.Case, async: true + + doctest Lua.Table +end diff --git a/test/lua/util_test.exs b/test/lua/util_test.exs index 3159158..a012ae1 100644 --- a/test/lua/util_test.exs +++ b/test/lua/util_test.exs @@ -1,5 +1,5 @@ defmodule Lua.UtilTest do - use ExUnit.Case + use ExUnit.Case, async: true doctest Lua.Util, import: true