Enum

There are no such thing as tagged union (enums from Rust) in Go. From Scale perspective, a enum is represented by a variant index that defines what enum variant is described and by additional data that each variant could have.

Simple Enums

Example:

type SimpleEnum struct {
	VariantIndex uint8
}

func (this SimpleEnum) ToString() string {
	switch this.VariantIndex {
	case 0:
		return "Nothing"
	case 1:
		return "Day"
	case 2:
		return "Month"
	case 3:
		return "Year"
	default:
		panic("Unknown SimpleEnum Variant Index")
	}
}

Simple enums don't have anything attach to it besides the variant index. The scale encoding and decoding is done automatically so there is no need to write our own encode/decode methods. In order to know how many variants there are and what each variant means, we create the ToString() method that shows thats.

In order to set the enum to a variant, we just need to set VariantIndex to the desired and correct value.

Example of correct setup:

enum := SimpleEnum{}
enum.VariantIndex = 1

Example of incorrect setup:

// VariantIndex out of range
enum := SimpleEnum{}
enum.VariantIndex = 200

Complex Enums

Example:

type ComplexEnum struct {
	VariantIndex uint8
	Day          prim.Option[uint16]
	Month        prim.Option[uint8]
	Year         prim.Option[uint32]
}

func (this ComplexEnum) ToString() string {
	switch this.VariantIndex {
	case 0:
		return "Nothing"
	case 1:
		return fmt.Sprintf("Set: %v", this.Day.Unwrap())
	case 2:
		return fmt.Sprintf("Set: %v", this.Month.Unwrap())
	case 3:
		return fmt.Sprintf("Set: %v", this.Year.Unwrap())
	default:
		panic("Unknown ComplexEnum Variant Index")
	}
}

func (this *ComplexEnum) EncodeTo(dest *string) {
	prim.Encoder.EncodeTo(this.VariantIndex, dest)

	if this.Day.IsSome() {
		prim.Encoder.EncodeTo(this.Day.Unwrap(), dest)
	}

	if this.Month.IsSome() {
		prim.Encoder.EncodeTo(this.Month.Unwrap(), dest)
	}

	if this.Year.IsSome() {
		prim.Encoder.EncodeTo(this.Year.Unwrap(), dest)
	}
}

func (this *ComplexEnum) Decode(decoder *prim.Decoder) error {
	*this = ComplexEnum{}

	if err := decoder.Decode(&this.VariantIndex); err != nil {
		return err
	}

	switch this.VariantIndex {
	case 0:
	case 1:
		var t uint16
		if err := decoder.Decode(&t); err != nil {
			return err
		}
		this.Day.Set(t)
	case 2:
		var t uint8
		if err := decoder.Decode(&t); err != nil {
			return err
		}
		this.Month.Set(t)
	case 3:
		var t uint32
		if err := decoder.Decode(&t); err != nil {
			return err
		}
		this.Year.Set(t)
	default:
		return errors.New("Unknown ComplexEnum Variant Index while Decoding")
	}

	return nil
}

When at least one variant has additional data attach to it, we are forced to created on our encode and decode methods.

First of all the additional variant data needs to be stored as an option, and the field member should have the same name as the variant itself. In this case Day, Month, Year now carry additional data and that's why there are three fields with the same name in our enum struct.

The EncodeTo method manually scale encodes the data. The VariantIndex is a u8 so it's going to be encoded like that. The rest depends on what option has been set. If VariantIndex is set to 1, and Day is set to 25, both will be encoded correctly. Take care: If you set up the wrong option or set up more than one option then the transaction will fail. It's up to you to be diligent and not mess up.

Example of correct setup:

enum := ComplexEnum{}
enum.VariantIndex = 2
enum.Month.Set(12)

Example of incorrect setup:

// VariantIndex out of range
enum := ComplexEnum{}
enum.VariantIndex = 125

// VariantIndex and data not matching
enum.VariantIndex = 0
enum.Year.Set(1990)

// Too many data fields are set
enum.VariantIndex = 1
enum.Day.Set(24)
enum.Year.Set(1990)

There isn't much room for errors in the Decode method unless the devs messed it up.