defmodule Oidcc.RecordStruct do @moduledoc false @callback record_to_struct(record :: tuple()) :: struct() @callback struct_to_record(struct :: struct()) :: tuple() defmacro __using__(options) do internal_name = Keyword.fetch!(options, :internal_name) record_name = Keyword.fetch!(options, :record_name) record_type_module = Keyword.get(options, :record_type_module, record_name) record_type_name = Keyword.get(options, :record_type_name, :t) hrl = Keyword.fetch!(options, :hrl) quote bind_quoted: [ internal_name: internal_name, record_name: record_name, record_type_module: record_type_module, record_type_name: record_type_name, hrl: hrl, behaviour: __MODULE__ ] do @behaviour behaviour require Record record = Record.extract(record_name, from: hrl) keys = :lists.map(&elem(&1, 0), record) vals = :lists.map(&{&1, [], nil}, keys) pairs = :lists.zip(keys, vals) Record.defrecordp(internal_name, record_name, record) defstruct record @doc false @impl behaviour @spec record_to_struct(record :: unquote(record_type_module).unquote(record_type_name)()) :: t() def record_to_struct(record), do: struct!(__MODULE__, unquote(internal_name)(record)) @doc false @impl behaviour @spec struct_to_record(struct :: t()) :: unquote(record_type_module).unquote(record_type_name)() def struct_to_record(%__MODULE__{unquote_splicing(pairs)}), do: {unquote(record_name), unquote_splicing(vals)} defoverridable record_to_struct: 1, struct_to_record: 1 end end end