defmodule Zig.Type.Array do alias Zig.Type use Type import Type, only: :macros defstruct [:child, :len, :repr, has_sentinel?: false, mutable: false] @type t :: %__MODULE__{ child: Type.t(), repr: String.t(), len: non_neg_integer, mutable: boolean, has_sentinel?: boolean } def from_json( %{"child" => child, "len" => len, "has_sentinel" => has_sentinel?, "repr" => repr}, module ) do %__MODULE__{ child: Type.from_json(child, module), len: len, has_sentinel?: has_sentinel?, repr: repr } end def to_string(%{mutable: true} = array), do: "*" <> to_string(%{array | mutable: false}) def to_string(%{has_sentinel?: true, repr: repr}), do: repr def to_string(array), do: "[#{array.len}]#{Kernel.to_string(array.child)}" def to_call(%{mutable: true} = array), do: "*" <> to_call(%{array | mutable: false}) def to_call(%{has_sentinel?: true, repr: repr}), do: repr def to_call(array), do: "[#{array.len}]#{Type.to_call(array.child)}" def return_allowed?(array), do: Type.return_allowed?(array.child) def spec(%{child: ~t(u8)} = type, :return, opts) do # u8 defaults to binary case Keyword.fetch!(opts, :type) do :list -> [Type.spec(~t(u8), :return, opts)] t when t in ~w(default binary)a -> binary_form(~t(u8), known_length(type)) end end def spec(%{child: child} = type, :return, opts) do # other types defaults to binary binary_form = binary_form(child, known_length(type)) case Keyword.fetch!(opts, :type) do :binary when not is_nil(binary_form) -> binary_form other when other in ~w(default binary list)a -> [Type.spec(child, :return, opts)] end end def spec(%{child: child} = type, :param, opts) do # u8 defaults to binary if binary_form = binary_form(child, known_length(type)) do quote context: Elixir do unquote([Type.spec(child, :param, opts)]) | unquote(binary_form) end else [Type.spec(child, :param, opts)] end end defp known_length(%{len: length, has_sentinel?: sentinel?}) do unless sentinel?, do: length end defp binary_form(~t(u8), nil), do: Type.spec(:binary) defp binary_form(%Type.Integer{bits: bits}, length) do if length do quote context: Elixir do <<_::unquote(Type.Integer._next_power_of_two_ceil(bits) * length)>> end else quote context: Elixir do <<_::_*unquote(Type.Integer._next_power_of_two_ceil(bits))>> end end end defp binary_form(%Type.Float{bits: bits}, length) do if length do quote context: Elixir do <<_::unquote(bits * length)>> end else quote context: Elixir do <<_::_*unquote(bits)>> end end end defp binary_form(%Type.Struct{packed: size}, length) when is_integer(size) do if length do quote context: Elixir do <<_::unquote(size * 8 * length)>> end else quote context: Elixir do <<_::_*unquote(size * 8)>> end end end defp binary_form(_, _), do: nil def of(type, len, opts \\ []) do struct(__MODULE__, opts ++ [child: type, len: len]) end end