defmodule Zig.Type.Manypointer do
alias Zig.Type
use Type
import Type, only: :macros
defstruct [:child, :repr, has_sentinel?: false]
@type t :: %__MODULE__{
child: Type.t(),
repr: String.t(),
has_sentinel?: boolean
}
def from_json(
%{"child" => child, "has_sentinel" => has_sentinel?, "repr" => repr},
module
) do
%__MODULE__{
child: Type.from_json(child, module),
has_sentinel?: has_sentinel?,
repr: repr
}
end
def to_string(%{has_sentinel?: true, repr: repr}), do: repr
def to_string(slice), do: "[*]#{Kernel.to_string(slice.child)}"
def to_call(%{has_sentinel?: true, repr: repr}), do: repr
def to_call(slice), do: "[*]#{Type.to_call(slice.child)}"
def return_allowed?(pointer), do: pointer.has_sentinel? and Type.return_allowed?(pointer.child)
def missing_size?(_), do: true
# only manypointers of [*:0]u8 are allowed to be returned.
def spec(%{child: ~t(u8), has_sentinel?: true}, :return, opts) do
case Keyword.fetch!(opts, :type) do
:list ->
[Type.spec(~t(u8), :return, opts)]
type when type in ~w(default binary)a ->
Type.spec(:binary)
end
end
def spec(%{child: child, has_sentinel?: sentinel}, :param, opts)
when not sentinel or child == ~t(u8) do
if binary_form = binary_form(child) do
quote context: Elixir do
unquote([Type.spec(child, :param, opts)]) | unquote(binary_form)
end
else
[Type.spec(child, :param, opts)]
end
end
defp binary_form(~t(u8)), do: Type.spec(:binary)
defp binary_form(%Type.Integer{bits: bits}) do
quote context: Elixir do
<<_::_*unquote(Type.Integer._next_power_of_two_ceil(bits))>>
end
end
defp binary_form(%Type.Float{bits: bits}) do
quote context: Elixir do
<<_::_*unquote(bits)>>
end
end
defp binary_form(%Type.Struct{packed: size}) when is_integer(size) do
quote context: Elixir do
<<_::_*unquote(size * 8)>>
end
end
defp binary_form(_), do: nil
def of(type, opts \\ []) do
struct(__MODULE__, opts ++ [child: type])
end
end