Current section
Files
Jump to
Current section
Files
lib/dextruct.ex
defmodule Dextruct do
@moduledoc """
Destructing assignment operator and utilities.
"""
@doc """
Dextruct operator `<~` imitate the destruction assignment behavior on other language like _Ruby_ and _ES6_.
"""
defmacro __using__(opts \\ []) do
quote do
import Dextruct, unquote(Keyword.drop(opts, [:fill]))
defmacro left <~ right when is_list(right) do
len = length(left)
elem = unquote(opts[:fill])
list = fill(right, len, elem)
quote do
unquote(left) = unquote(list)
end
end
defmacro left <~ right do
elem = unquote(opts[:fill])
keys_or_length = left
|> Macro.expand(__ENV__)
|> fetch_keys_or_length
quote do
unquote(left) = fill(unquote(right), unquote(keys_or_length), unquote(elem))
end
end
end
end
@doc """
Hello world.
## Examples
iex> Dextruct.fill([1], 3)
[1, nil, nil]
## Example
iex> Dextruct.fill(%{a: 1}, [:a, :b, :c])
%{a: 1, b: nil, c: nil}
"""
@spec fill(Enum.t, integer | list, any) :: Enum.t
def fill(enum, length_or_keys, elem \\ nil)
def fill(list, length, elem) when is_list(list) do
filler = List.duplicate(elem, length)
list
|> :erlang.++(filler)
|> Enum.take(length)
end
def fill(map, keys, elem) when is_map(map) do
filler = Map.new(keys, fn k -> {k, elem} end)
Map.merge(filler, map)
end
@doc """
"""
defmacro sigil_m({:<<>>, _line, [string]}, opt) do
ast = string
|> String.split(~r/,(?:\s)*/)
|> Enum.map(&String.split(&1, ~r/(:\s|\s=>\s)/))
|> Enum.map(&to_variable_ast(&1, opt))
{:%{}, [], ast}
end
defp to_variable_ast([key], _opt) do
k = String.to_atom(key)
# unbinding variable is unhygienic
{k, {k, [], nil}}
end
defp to_variable_ast([key, val], _opt) do
{String.to_atom(key), {String.to_atom(val), [], nil}}
end
def fetch_keys_or_length({:%{}, _, args}) do
Keyword.keys(args)
end
def fetch_keys_or_length(list) when is_list(list) do
length(list)
end
def fetch_keys_or_length(_ast), do: raise SyntaxError
end