enum

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2023 License: MIT Imports: 5 Imported by: 0

README

go-struct-enum

Go Version GoDoc Go Report Card Sourcegraph Tag Contributors License

A fully featured Golang struct based enums. It provides a simple way to declare enums and use them in structs.

Enums are harden against:

  • Arithmetics operations
  • Comparison operators except == and !=
  • Implicit cast of untyped constants (use the based type instead of the enum type)

An enum implements the following interfaces:

  • fmt.Stringer
  • json.Marshaler
  • json.Unmarshaler
  • sql.Scanner
  • driver.Valuer

Install

Download it:

$ go get github.com/FabienMht/go-struct-enum

Add the following import:

enum "github.com/FabienMht/go-struct-enum"

Usage

Basic

Checkout the detailed example in the documentation for more information.

Declare enum values:

var (
	// Define States
	TestStateUnknown = &TestState{enum.New("")}
	TestStatePassed  = &TestState{enum.New("passed")}
	TestStateSkipped = &TestState{enum.New("skipped")}
	TestStateFailed  = &TestState{enum.New("failed")}

	// Define the ordered list of states
	TestStates = []enum.Enummer[string]{
		TestStateUnknown,
		TestStatePassed,
		TestStateSkipped,
		TestStateFailed,
	}

	// Define states parsers
	ParseTestState     = enum.Parse(TestStates)
	MustParseTestState = enum.MustParse(TestStates)
)

// Define the state enum
type TestState struct {
	enum.Enum[string]
}

Use enum values:

// Parse a state
state := ParseTestState("passed") // TestStatePassed
// Or panic if the state is not valid
state := MustParseTestState("passed") // TestStatePassed

// Get the state value
state.Value() // "passed"
// Check the enum value
state.EqualValue("passed") // true

Use enum in structs:

type Test struct {
    State *TestState `json:"state"`
}

// Marshal a struct with an enum
json.Marshal(&Test{State: TestStatePassed}) // {"state":"passed"}

// Unmarshal a struct with an enum
var test Test
json.Unmarshal([]byte(`{"state":"passed"}`), &test) // &Test{State: TestStatePassed}
Comparison

Checkout the detailed example in the documentation for more information.

Define comparison functions:

// Define the state enum
type TestState struct {
	enum.Enum[string]
}

func (ts *TestState) Equal(other *TestState) bool {
	return enum.Equal(ts, other)
}

func (ts *TestState) GreaterThan(other *TestState) bool {
	return enum.GreaterThan(TestStates)(ts, other)
}

func (ts *TestState) LessThan(other *TestState) bool {
	return enum.LessThan(TestStates)(ts, other)
}

func (ts *TestState) GreaterThanOrEqual(other *TestState) bool {
	return enum.GreaterThanOrEqual(TestStates)(ts, other)
}

func (ts *TestState) LessThanOrEqual(other *TestState) bool {
	return enum.LessThanOrEqual(TestStates)(ts, other)
}

Use comparison functions:

TestStatePassed.Equal(TestStatePassed) // true
TestStatePassed.Equal(TestStateFailed) // false
TestStatePassed.GreaterThan(TestStatePassed) // false
TestStatePassed.GreaterThan(TestStateFailed) // false
TestStatePassed.GreaterThanOrEqual(TestStatePassed) // true
TestStatePassed.GreaterThanOrEqual(TestStateFailed) // false
TestStatePassed.LessThan(TestStatePassed) // false
TestStatePassed.LessThan(TestStateFailed) // true
TestStatePassed.LessThanOrEqual(TestStatePassed) // true
TestStatePassed.LessThanOrEqual(TestStateFailed) // true

Benchmark

$ task bench
task: [bench] go test -bench=. -benchmem
goos: linux
goarch: amd64
pkg: github.com/FabienMht/go-struct-enum
cpu: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
BenchmarkParse-8                                15764276                77.69 ns/op           48 B/op          1 allocs/op
BenchmarkParsePrealloc-8                        383380515                3.034 ns/op           0 B/op          0 allocs/op
BenchmarkEqual-8                                63047527                16.52 ns/op            0 B/op          0 allocs/op
BenchmarkGreaterThan-8                           6585123               187.9 ns/op            48 B/op          1 allocs/op
BenchmarkGreaterThanPrealloc-8                  10859983               108.9 ns/op             0 B/op          0 allocs/op
BenchmarkGreaterThanOrEqual-8                    6594724               184.5 ns/op            48 B/op          1 allocs/op
BenchmarkGreaterThanOrEqualPrealloc-8           10677360               107.0 ns/op             0 B/op          0 allocs/op
BenchmarkLessThan-8                              6341217               183.0 ns/op            48 B/op          1 allocs/op
BenchmarkLessThanPrealloc-8                     10930384               107.0 ns/op             0 B/op          0 allocs/op
BenchmarkLessThanOrEqualThan-8                   6474268               182.1 ns/op            48 B/op          1 allocs/op
BenchmarkLessThanOrEqualThanPrealloc-8          10892846               107.9 ns/op             0 B/op          0 allocs/op
PASS
ok      github.com/FabienMht/go-struct-enum     14.543s

Contributing

Contributions are welcome ! Please open an issue or submit a pull request.

# Install task
$ go install github.com/go-task/task/v3/cmd/task@v3

# Install dev dependencies
$ task dev

# Run linter
$ task lint

# Run tests
$ task test

# Run benchmarks
$ task bench

Documentation

Overview

Example
package main

import (
	"encoding/json"
	"fmt"

	enum "github.com/FabienMht/go-struct-enum"
)

var (
	// Define States
	TestStateUnknown = &TestState{enum.New("")}
	TestStatePassed  = &TestState{enum.New("passed")}
	TestStateSkipped = &TestState{enum.New("skipped")}
	TestStateFailed  = &TestState{enum.New("failed")}

	// Define the ordered list of states
	TestStates = []enum.Enummer[string]{
		TestStateUnknown,
		TestStatePassed,
		TestStateSkipped,
		TestStateFailed,
	}

	// Define states parsers
	ParseTestState     = enum.Parse(TestStates)
	MustParseTestState = enum.MustParse(TestStates)
)

// Define the state enum
type TestState struct {
	enum.Enum[string]
}

func main() {
	// Recover from MustParseTestState panic
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("recovered from panic:", r)
		}
	}()

	// Basic usage
	fmt.Println(TestStatePassed)
	fmt.Println(TestStatePassed.String())
	fmt.Println(TestStatePassed.GetValue())
	fmt.Println(TestStatePassed.EqualValue("passed"))
	fmt.Println(TestStatePassed.EqualValue("failed"))

	// JSON marshaling and unmarshalling
	fmt.Println(json.Marshal(TestStatePassed))
	var result *TestState
	json.Unmarshal([]byte("\"passed\""), &result)
	fmt.Println(result)

	// Parse string into enum
	fmt.Println(ParseTestState("passed"))
	fmt.Println(ParseTestState("xxx"))
	fmt.Println(MustParseTestState("passed"))
	fmt.Println(MustParseTestState("xxx"))

}
Output:

passed
passed
passed
true
false
[34 112 97 115 115 101 100 34] <nil>
passed
passed
<nil>
passed
recovered from panic: enum: 'xxx' not found
Example (Compare)
package main

import (
	"encoding/json"
	"fmt"

	enum "github.com/FabienMht/go-struct-enum"
)

var (
	// Define States
	TestStateCompareUnknown = &TestStateCompare{enum.New("")}
	TestStateComparePassed  = &TestStateCompare{enum.New("passed")}
	TestStateCompareSkipped = &TestStateCompare{enum.New("skipped")}
	TestStateCompareFailed  = &TestStateCompare{enum.New("failed")}

	// Define the ordered list of states
	// Higher states in the list are considered greater than lower states
	TestStateCompares = []enum.Enummer[string]{
		TestStateCompareUnknown,
		TestStateComparePassed,
		TestStateCompareSkipped,
		TestStateCompareFailed,
	}

	// Define states parsers
	ParseTestStateCompare     = enum.Parse(TestStateCompares)
	MustParseTestStateCompare = enum.MustParse(TestStateCompares)
)

// Define the state enum
type TestStateCompare struct {
	enum.Enum[string]
}

func (ts *TestStateCompare) Equal(other *TestStateCompare) bool {
	return enum.Equal(ts, other)
}

func (ts *TestStateCompare) GreaterThan(other *TestStateCompare) bool {
	return enum.GreaterThan(TestStateCompares)(ts, other)
}

func (ts *TestStateCompare) LessThan(other *TestStateCompare) bool {
	return enum.LessThan(TestStateCompares)(ts, other)
}

func (ts *TestStateCompare) GreaterThanOrEqual(other *TestStateCompare) bool {
	return enum.GreaterThanOrEqual(TestStateCompares)(ts, other)
}

func (ts *TestStateCompare) LessThanOrEqual(other *TestStateCompare) bool {
	return enum.LessThanOrEqual(TestStateCompares)(ts, other)
}

func main() {
	// Recover from MustParseTestStateCompare panic
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("recovered from panic:", r)
		}
	}()

	// Basic usage
	fmt.Println(TestStateComparePassed)
	fmt.Println(TestStateComparePassed.String())
	fmt.Println(TestStateComparePassed.GetValue())
	fmt.Println(TestStateComparePassed.EqualValue("passed"))
	fmt.Println(TestStateComparePassed.EqualValue("failed"))

	// JSON marshaling and unmarshalling
	fmt.Println(json.Marshal(TestStateComparePassed))
	var result *TestStateCompare
	json.Unmarshal([]byte("\"passed\""), &result)
	fmt.Println(result)

	// Comparison
	fmt.Println(TestStateComparePassed.Equal(TestStateComparePassed))
	fmt.Println(TestStateComparePassed.Equal(TestStateCompareFailed))
	fmt.Println(TestStateComparePassed.GreaterThan(TestStateComparePassed))
	fmt.Println(TestStateComparePassed.GreaterThan(TestStateCompareFailed))
	fmt.Println(TestStateComparePassed.GreaterThanOrEqual(TestStateComparePassed))
	fmt.Println(TestStateComparePassed.GreaterThanOrEqual(TestStateCompareFailed))
	fmt.Println(TestStateComparePassed.LessThan(TestStateComparePassed))
	fmt.Println(TestStateComparePassed.LessThan(TestStateCompareFailed))
	fmt.Println(TestStateComparePassed.LessThanOrEqual(TestStateComparePassed))
	fmt.Println(TestStateComparePassed.LessThanOrEqual(TestStateCompareFailed))

	// Parse string into enum
	fmt.Println(ParseTestStateCompare("passed"))
	fmt.Println(ParseTestStateCompare("xxx"))
	fmt.Println(MustParseTestStateCompare("passed"))
	fmt.Println(MustParseTestStateCompare("xxx"))

}
Output:

passed
passed
passed
true
false
[34 112 97 115 115 101 100 34] <nil>
passed
true
false
false
false
true
false
false
true
true
true
passed
<nil>
passed
recovered from panic: enum: 'xxx' not found

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Equal

func Equal[T ~int | ~string](a, b Enummer[T]) bool

Equal returns true if the first Enummer is equal to the second Enummer. It panics if the Enummer are not of the same type.

func GreaterThan

func GreaterThan[T ~int | ~string](list []Enummer[T]) func(Enummer[T], Enummer[T]) bool

GreaterThan returns true if the first Enummer is greater than the second Enummer. It takes the Enummer list that defines the order and returns a function. Higher indices are considered higher than lower indices. It panics if Enummers are not of the same type or if Enummers are not in the list.

func GreaterThanOrEqual

func GreaterThanOrEqual[T ~int | ~string](list []Enummer[T]) func(Enummer[T], Enummer[T]) bool

GreaterThanOrEqual returns true if the first Enummer is greater than or equal to the second Enummer. It takes the Enummer list that defines the order and returns a function. Higher indices are considered higher than lower indices. It panics if Enummers are not of the same type or if Enummers are not in the list.

func LessThan

func LessThan[T ~int | ~string](list []Enummer[T]) func(Enummer[T], Enummer[T]) bool

LessThan returns true if the first Enummer is less than the second Enummer. It takes the Enummer list that defines the order and returns a function. Higher indices are considered higher than lower indices. It panics if Enummers are not of the same type or if Enummers are not in the list.

func LessThanOrEqual

func LessThanOrEqual[T ~int | ~string](list []Enummer[T]) func(Enummer[T], Enummer[T]) bool

LessThanOrEqual returns true if the first Enummer is less than or equal to the second Enummer. It takes the Enummer list that defines the order and returns a function. Higher indices are considered higher than lower indices. It panics if Enummers are not of the same type or if Enummers are not in the list.

func MustParse

func MustParse[T ~int | ~string](list []Enummer[T]) func(T) Enummer[T]

MustParse parses the given string/int into an Enummer. It takes an Enummer list and returns a function that takes the given string/int and returns the Enummer. It panics if the Enummer in list are not of the same type or if the list is empty. If the string/int is not found, it panics.

func Parse

func Parse[T ~int | ~string](list []Enummer[T]) func(T) Enummer[T]

Parse parses the given string/int into an Enummer. It takes an Enummer list and returns a function that takes the given string/int and returns the Enummer. It panics if the Enummer in list are not of the same type or if the list is empty. If the string/int is not found, it returns nil.

Types

type Enum

type Enum[T ~int | ~string] struct {
	// contains filtered or unexported fields
}

Enum is a generic type used to create enums.

func New

func New[T ~int | ~string](val T) Enum[T]

New creates a new enum with the given value. The result must be embedded into a struct. The embedding struct must be a pointer to implement the Enummer interface.

Example:

type TestState struct {
	enum.Enum[string]
}
TestStatePassed = &TestState{enum.New("passed")}

func (Enum[T]) EqualValue

func (e Enum[T]) EqualValue(other T) bool

EqualValue returns true if the enums underlying value are equal.

func (Enum[T]) GetValue

func (e Enum[T]) GetValue() T

GetValue returns the enum underlying value.

func (Enum[T]) MarshalJSON

func (e Enum[T]) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (*Enum[T]) Scan

func (e *Enum[T]) Scan(value interface{}) error

Scan implements the sql.Scanner interface.

func (Enum[T]) String

func (e Enum[T]) String() string

String returns the string representation of the enum underlying value.

func (*Enum[T]) UnmarshalJSON

func (e *Enum[T]) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaler interface.

func (Enum[T]) Value

func (e Enum[T]) Value() (driver.Value, error)

Value implements the driver.Valuer interface.

type Enummer

type Enummer[T ~int | ~string] interface {
	String() string
	GetValue() T
	EqualValue(other T) bool
	MarshalJSON() ([]byte, error)
	UnmarshalJSON(data []byte) error
	Scan(value interface{}) error
	Value() (driver.Value, error)
}

Enummer is an interface that represents an enum. Type ~int is the set of all types whose underlying type is int. Type ~string is the set of all types whose underlying type is string.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL