lib/hugs/struct_definition.ex

defmodule Hugs.StructDefinition do
  alias Hugs.FieldDefinition
  alias Hugs.PropsDefinition

  @enforce_keys [:module, :props]
  defstruct @enforce_keys

  def new(module) do
    %__MODULE__{module: module, props: PropsDefinition.new()}
  end

  def add_field(%__MODULE__{props: m} = this, %FieldDefinition{} = field) do
    verify_struct_field!(this, field)
    %__MODULE__{this | props: PropsDefinition.add_field(m, field)}
  end

  def add_constraint(%__MODULE__{props: m} = this, module, fun, args) do
    %__MODULE__{this | props: PropsDefinition.add_constraint(m, module, fun, args)}
  end

  def constraints(%__MODULE__{props: m}), do: PropsDefinition.constraints(m)

  defp verify_struct_field!(_this, %FieldDefinition{} = field) do
    %FieldDefinition{key: key, required: _req?, default: _default, optional: opt?} = field

    if not is_atom(key),
      do: raise("only atoms are supported for struct keys, got: #{inspect(key)}")

    if opt?,
      do: raise("struct fields cannot be :optional")

    # if not req? and default == :__undef__ do
    #   IO.warn(
    #     "field #{inspect(key)} of struct #{inspect(this.module)} is not required and was not given a default value. :__undef__ base default value will be used"
    #   )
    # end
  end

  def required_keys(%__MODULE__{props: m}), do: PropsDefinition.required_keys(m)
  def default_pairs(%__MODULE__{props: m}), do: PropsDefinition.default_pairs(m, false)
end