From c198dc7115ebc70f93de3fd04dfb8037d446f81d Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Mon, 18 Mar 2024 16:45:07 -0400 Subject: [PATCH 1/2] Utilities for converting Lua tables into Elixir datastructures Adds the module `Lua.Table` which provides utilities for working with Lua tables, converting them into more convenient datastructures --- lib/lua/table.ex | 76 +++++++++++++++++++++++++++++++++++++++++ test/lua/api_test.exs | 2 +- test/lua/table_test.exs | 5 +++ test/lua/util_test.exs | 2 +- 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 lib/lua/table.ex create mode 100644 test/lua/table_test.exs diff --git a/lib/lua/table.ex b/lib/lua/table.ex new file mode 100644 index 0000000..2947546 --- /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` options + + 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 + &Enum.sort/1 + 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 will use 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 From 508711bcc2de576093a7a0811c4f2db2c45e9327 Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Mon, 18 Mar 2024 16:52:29 -0400 Subject: [PATCH 2/2] better sort, docs --- lib/lua/table.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/lua/table.ex b/lib/lua/table.ex index 2947546..65f0485 100644 --- a/lib/lua/table.ex +++ b/lib/lua/table.ex @@ -14,7 +14,7 @@ defmodule Lua.Table do 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` options + 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"] @@ -28,7 +28,7 @@ defmodule Lua.Table do sorter = if Keyword.fetch!(opts, :sort) do - &Enum.sort/1 + &List.keysort(&1, 0) else &Function.identity/1 end @@ -52,7 +52,7 @@ defmodule Lua.Table do Converts a Lua table into more "native" feeling lists and maps, deeply traversing any sub-tables. - It will use the heuristic that maps with integer keys starting + 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}]}])