Skip to content

Instantly share code, notes, and snippets.

@michalmuskala
Created June 14, 2017 18:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michalmuskala/c5d94fc18467b87d25011944875c3f35 to your computer and use it in GitHub Desktop.
Save michalmuskala/c5d94fc18467b87d25011944875c3f35 to your computer and use it in GitHub Desktop.
defmodule SafeNilGet do
defmacro sng(ast) do
do_sng(ast)
end
defp do_sng({_v, _meta, context} = ast) when is_atom(context), do: ast
defp do_sng({:__aliases__, _ameta, [v, :All]}), do: do_sng(v)
defp do_sng({:., _dotmeta, [{:__aliases__, _ameta, [v, :All]}]}), do: do_sng(v)
defp do_sng({:., _dotmeta, [{:__aliases__, ameta, [v, :All]}, k]}) when is_atom(k) do
elem = quote(do: elem)
quote do
case unquote(do_sng(v)) do
nil -> nil
enum -> for elem <- enum, elem, do: unquote(do_sng({:., ameta, [elem, k]}))
end
end
end
defp do_sng({:., _meta, [v, k]}) when is_atom(k) do
idx =
with "_" <> sidx <- to_string(k),
{idx, ""} <- Integer.parse(sidx) do
idx
else
_ -> -1
end
quote do
case unquote(do_sng(v)) do
nil -> nil
map when is_map(map) -> Map.get(map, unquote(k), nil)
other when is_list(other) or is_tuple(other) -> Enum.at(other, unquote(idx), nil)
end
end
end
defp do_sng({:., _meta, [v]}), do: do_sng(v)
defp do_sng({v, _meta, [fun_ast]}) do
quote do
case unquote(do_sng(v)) do
nil -> nil
enum -> Enum.filter(enum, unquote(fun_ast))
end
end
end
defp do_sng({v, _meta, []}), do: do_sng(v)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment