Skip to content

daichitakahashi/go-enum

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-enum

Idea from following video.
列挙型の作り方を再考する - Go Conference 2023 Online

This package provides support for defining Visitor and implementing Accept() methods.

Usage

Examples here.

  1. Prepare enum interface and member types.
package fruits

import "github.com/daichitakahashi/go-enum"

type (
	// enum interface
	Fruits interface{}

	// member types
	Apple  enum.MemberOf[Fruits]
	Orange enum.MemberOf[Fruits]
	Grape  enum.MemberOf[Fruits]
)
  1. Run following command.
$ go run github.com/daichitakahashi/go-enum/cmd/enumgen
  1. Following code is generated with the file name enum.gen.go(file name is configurable).
// Code generated by enumgen. DO NOT EDIT.

package fruits

type (
	FruitsVisitor interface {
		VisitApple(e Apple)
		VisitOrange(e Orange)
		VisitGrape(e Grape)
	}
	FruitsEnum interface {
		Accept(v FruitsVisitor)
	}
)

func (e Apple) Accept(v FruitsVisitor) {
	v.VisitApple(e)
}
func (e Orange) Accept(v FruitsVisitor) {
	v.VisitOrange(e)
}
func (e Grape) Accept(v FruitsVisitor) {
	v.VisitGrape(e)
}

var _ = []FruitsEnum{Apple{}, Orange{}, Grape{}}
  1. Optional: Embed generated FruitsEnum type in Fruits type.
type (
	// enum interface
	Fruits interface{
		FruitsEnum
	}
...
  1. Implement your visitor type!

Options for enumgen

option description default value
--wd working directory .
--out output file name enum.gen.go
--visitor customize Visitor type & method names *:*Visitor:Visit*
--accept customize Accept method name *:Accept
--visitor-impl generate Visitor implementation and its factory

--visitor option

The value of --visitor option consists of three parts with the delimiter ":".

  1. The target type name(enum identifier interface) of customization.
    Pattern match using * is allowed.
  2. The visitor type name pattern.
    If the pattern contains *, it will replaced with the target type name.
  3. The visit method name pattern.
    If the pattern contains *, it will replaced with the member type name.

--accept option

The value of --accept option consists of two parts with the delimiter ":".

  1. The target type name(enum identifier interface) of customization.
    Pattern match using * is allowed.
  2. The accept method name pattern.
    If the pattern contains *, it will replaced with the target type name.

--visitor and --accept options can be used multiple times.

--visitor-impl option

The value of --visitor-impl option consists of one part or two parts with the delimiter ":".

  1. The target type name(enum identifier interface) to implement.
    Pattern match using * is allowed.
  2. The factory function name pattern(if omitted, use "New*").
    If the pattern contains *, it will replaced with the target type name.

Return type of visitor method.

If you need return type T of visitor (and accept) methods, embed enum.VisitorReturns[T] to enum identifier interface.

type Fruits interface {
	enum.VisitorReturns[error]
}

Preceding enum identifier derives following code.

type (
	FruitsVisitor interface {
		VisitApple(e Apple) error
		VisitOrange(e Orange) error
		VisitGrape(e Grape) error
	}
	FruitsEnum interface {
		Accept(v FruitsVisitor) error
	}
)

func (e Apple) Accept(v FruitsVisitor) error {
	return v.VisitApple(e)
}
func (e Orange) Accept(v FruitsVisitor) error {
	return v.VisitOrange(e)
}
func (e Grape) Accept(v FruitsVisitor) error {
	return v.VisitGrape(e)
}

Example: use enumgen for domain event handler.

package event

import (
	"time"

	"github.com/daichitakahashi/go-enum"
)

//go:generate go run github.com/daichitakahashi/go-enum/cmd/enumgen@latest --visitor="Event:EventHandler:On*" --accept="Event:Emit" --visitor-impl="*"

type (
	Event interface {
		ID() string
		enum.VisitorReturns[error]
	}

	OrderPlaced struct {
		enum.MemberOf[Event]
		Items []string
	}

	PaymentReceived struct {
		enum.MemberOf[Event]
		Amount int
	}

	ItemShipped struct {
		enum.MemberOf[Event]
		ShippedAt time.Time
	}
)

// ID implements Event.
func (OrderPlaced) ID() string {
	return "orderPlaced"
}

// ID implements Event.
func (PaymentReceived) ID() string {
	return "paymentReceived"
}

// ID implements Event.
func (ItemShipped) ID() string {
	return "itemShipped"
}

var (
	_ Event = OrderPlaced{}
	_ Event = PaymentReceived{}
	_ Event = ItemShipped{}
)

go generated:

// Code generated by enumgen. DO NOT EDIT.

package event

type (
	EventHandler interface {
		OnOrderPlaced(e OrderPlaced)
		OnPaymentReceived(e PaymentReceived)
		OnItemShipped(e ItemShipped)
	}
	EventEnum interface {
		Emit(v EventHandler)
	}
)

func (e OrderPlaced) Emit(v EventHandler) {
	v.OnOrderPlaced(e)
}
func (e PaymentReceived) Emit(v EventHandler) {
	v.OnPaymentReceived(e)
}
func (e ItemShipped) Emit(v EventHandler) {
	v.OnItemShipped(e)
}

var _ = []EventEnum{OrderPlaced{}, PaymentReceived{}, ItemShipped{}}

type __EventHandler struct {
	__OnOrderPlaced     func(OrderPlaced) error
	__OnPaymentReceived func(PaymentReceived) error
	__OnItemShipped     func(ItemShipped) error
}

func NewEventHandler(__OnOrderPlaced func(e OrderPlaced) error, __OnPaymentReceived func(e PaymentReceived) error, __OnItemShipped func(e ItemShipped) error) EventHandler {
	return &__EventHandler{__OnOrderPlaced: __OnOrderPlaced, __OnPaymentReceived: __OnPaymentReceived, __OnItemShipped: __OnItemShipped}
}
func (v __EventHandler) OnOrderPlaced(e OrderPlaced) error {
	return v.__OnOrderPlaced(e)
}
func (v __EventHandler) OnPaymentReceived(e PaymentReceived) error {
	return v.__OnPaymentReceived(e)
}
func (v __EventHandler) OnItemShipped(e ItemShipped) error {
	return v.__OnItemShipped(e)
}