Created
December 6, 2017 23:41
-
-
Save benfalk/6e5b6c080a97234d9a0e4acb738bd9fd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Packetx do | |
@moduledoc """ | |
Documentation for Packetx. | |
""" | |
defmacro __using__([do: block]) do | |
quote do | |
Module.register_attribute(__MODULE__, :struct_fields, accumulate: true) | |
Module.register_attribute(__MODULE__, :binary_matches, accumulate: true) | |
try do | |
import Packetx | |
unquote(block) | |
after | |
:ok | |
end | |
Module.eval_quoted __ENV__, [ | |
Packetx.__defstruct__(@struct_fields), | |
Packetx.__def_to_binary__(@binary_matches |> Enum.reverse), | |
Packetx.__def_from_binary__(@binary_matches |> Enum.reverse) | |
] | |
end | |
end | |
defmacro version(version_string) do | |
version = Version.parse!(version_string) |> Macro.escape | |
quote do | |
def version, do: unquote(version) | |
end | |
end | |
defmacro field(name, type, opts \\ []) do | |
quote do | |
Module.put_attribute(__MODULE__, :struct_fields, {unquote(name), nil}) | |
Module.put_attribute(__MODULE__, :binary_matches, {unquote(name), unquote(type), unquote(opts)}) | |
end | |
end | |
def __defstruct__(fields) do | |
quote do | |
defstruct unquote(Macro.escape(fields)) | |
end | |
end | |
def __def_to_binary__(matches) do | |
Enum.join([ | |
"def to_binary(%__MODULE__{#{to_bin_matches(matches)}}) do", | |
"<<#{to_bin_binaries(matches)}>>", | |
"end"], "\n") | |
|> Code.string_to_quoted! | |
end | |
def __def_from_binary__(matches) do | |
Enum.join([ | |
"def from_binary(<<#{from_bin_matches(matches)}>>) do", | |
"%__MODULE__{#{to_bin_matches(matches)}}", | |
"end"], "\n") | |
|> Code.string_to_quoted! | |
end | |
## God Help Me | |
defp to_bin_matches(binary_matches) do | |
binary_matches | |
|> Enum.map(fn {name, _, _} -> "#{name}: #{name}" end) | |
|> Enum.join(", ") | |
end | |
defp to_bin_binaries(binary_matches) do | |
binary_matches | |
|> Enum.map(&build_bin/1) | |
|> Enum.join(", ") | |
end | |
defp build_bin({name, :integer, opts}) do | |
[bits: size] = Keyword.get(opts, :size, [bits: 8]) | |
"#{name}::#{size}" | |
end | |
defp build_bin({name, :pascal_string, _}) do | |
"byte_size(#{name})::8, #{name}::binary" | |
end | |
defp from_bin_matches(matches) do | |
matches | |
|> Enum.map(&from_bin_match/1) | |
|> Enum.join(", ") | |
end | |
defp from_bin_match({name, :integer, opts}) do | |
[bits: size] = Keyword.get(opts, :size, [bits: 8]) | |
"#{name}::#{size}" | |
end | |
defp from_bin_match({name, :pascal_string, _}) do | |
"#{name}_len__::8, #{name}::bytes-size(#{name}_len__)" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment