saleyn / typedstruct

An Elixir library for defining structs and records with a type without writing boilerplate code.
MIT License
13 stars 4 forks source link

Default Plugins #5

Open halostatue opened 2 weeks ago

halostatue commented 2 weeks ago

It would be nice to have a way to set default plugins for derived variations. I’m trying to make a typedstruct wrapper that also implements Ecto.Type. Something like:

defmodule MyApp.Type do
  defmacro __using__(_) do
    quote do
      use Ecto.Type
      use Typedstruct
      # use TypedStruct, plugins: [MyApp.Typestruct.Plugin.EctoTypes]

      @behaviour MyApp.Type

      @impl Ecto.Type
      def type, do: :map
      # …
    end
  end

  @callback changeset(struct, map) :: Ecto.Changeset.t()
end

Right now, I would need to do:

typedstruct do
  plugin MyApp.Typestruct.Plugin.EctoTypes

  field :code, String.t(), ecto_type: :string
end

With this, I would be able to do:

typedestruct do
  field :code, String.t(), ecto_type: :string
end

I could probably muck around with the block AST by overriding typedstruct/1,2, and I’ll be trying that, but it feels like specifying default plugins for a project like this would be a desirable feature overall.

halostatue commented 2 weeks ago

This may be a little harder than I thought, since the typedstruct macro probably won't have access to anything set in the __using__/1 call.

I basically did:

defmodule MyApp.EctoMapType
  defmacro __using__(_) do
    quote do
      use Ecto.Type
      import MyApp.EctoMapType, only: [typedstruct: 1, typedstruct: 2]
      …
    end
  end

  defmacro typedstruct(opts \\ [], do: block) do
    plugin_line = {:plugin, [], [{:__aliases__}, [], [:MyApp, :TypedStruct, :EctoStructPlugin]}]}
    block =
      case block do
        {:__block__, opts, lines} -> {:__block__, opts, [plugin_line | lines]}
        {_, _, _} -> {:__block__, [], [plugin_line, block]}
      end

    quote do
      require TypedStruct
      TypedStruct.typedstruct(unquote(opts), do: unquote(block))
    end
  end
end

That second line in the block reshaping for a single-field struct.