lib/codegen/nif.ex

defmodule Kinda.CodeGen.NIF do
  alias Kinda.CodeGen.{Function, Type}
  @type dirty() :: :io | :cpu | false
  @type t() :: %__MODULE__{
          wrapper_name: nil | String.t(),
          zig_name: nil | String.t(),
          nif_name: nil | String.t(),
          arity: integer(),
          ret: String.t(),
          dirty: dirty()
        }
  defstruct zig_name: nil, nif_name: nil, arity: 0, ret: nil, dirty: false, wrapper_name: nil

  def from_function(%Function{name: name, args: args, ret: ret}) do
    %__MODULE__{
      wrapper_name: name,
      zig_name: name,
      arity: length(args),
      ret: ret
    }
  end

  # TODO: make this extensible
  def from_resource_kind(%Type{module_name: module_name, kind_functions: kind_functions}) do
    for {f, a} <-
          [
            ptr: 1,
            ptr_to_opaque: 1,
            opaque_ptr: 1,
            array: 1,
            mut_array: 1,
            primitive: 1,
            make: 1,
            dump: 1,
            make_from_opaque_ptr: 2,
            array_as_opaque: 1
          ] ++ kind_functions do
      %__MODULE__{
        nif_name: Module.concat(module_name, f),
        arity: a
      }
    end
  end

  # if zig_name is nil this NIF should be registered by other way. For instance, functions of resource kinds.
  def gen(%__MODULE__{zig_name: nil}), do: ""

  def gen(%__MODULE__{zig_name: zig_name, nif_name: nif_name, arity: arity, dirty: dirty}) do
    dirty_flag =
      case dirty do
        :cpu -> "1"
        :io -> "2"
        false -> "0"
      end

    """
    e.ErlNifFunc{.name = "#{nif_name}", .arity = #{arity}, .fptr = #{zig_name}, .flags = #{dirty_flag}},
    """
  end
end