diff --git a/go.mod b/go.mod index a67b44cc7..aee2d9b9d 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/lib/pq v1.10.9 github.com/mitchellh/go-homedir v1.1.0 github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007 + github.com/orsinium-labs/enum v1.3.0 github.com/quic-go/quic-go v0.38.1 github.com/sirupsen/logrus v1.9.3 github.com/tylerb/graceful v1.2.15 diff --git a/go.sum b/go.sum index 35d76da69..ce178569e 100644 --- a/go.sum +++ b/go.sum @@ -94,6 +94,7 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= +github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= github.com/mattetti/filebuffer v1.0.0/go.mod h1:X6nyAIge2JGVmuJt2MFCqmHrb/5IHiphfHtot0s5cnI= github.com/mattetti/filebuffer v1.0.1 h1:gG7pyfnSIZCxdoKq+cPa8T0hhYtD9NxCdI4D7PTjRLM= github.com/mattetti/filebuffer v1.0.1/go.mod h1:YdMURNDOttIiruleeVr6f56OrMc+MydEnTcXwtkxNVs= @@ -122,6 +123,8 @@ github.com/nathan-fiscaletti/consolesize-go v0.0.0-20220204101620-317176b6684d/g github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/orsinium-labs/enum v1.3.0 h1:OsIMdDbY06X4N4urfk/ysMATuByK3I8troJ754XphDM= +github.com/orsinium-labs/enum v1.3.0/go.mod h1:Qj5IK2pnElZtkZbGDxZMjpt7SUsn4tqE5vRelmWaBbc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= diff --git a/platformdep/logo_unix.go b/platformdep/logo_unix.go index 665871d23..ff91ef53a 100644 --- a/platformdep/logo_unix.go +++ b/platformdep/logo_unix.go @@ -8,6 +8,8 @@ import ( "encoding/base64" "io" "strings" + + "github.com/orsinium-labs/enum" ) /* @@ -27,17 +29,27 @@ ANSI banner HOWTO */ -// gopher eyes -const image = `H4sIAJB76FoCA9WWTRKDIAxG91zBjUcQSBTGo/QM3n/bbvozfFRCRGp3nW+q5PEwZLhRWHm17LdxHG4Ut+GZJIHz9oeJtUu3xDGXk/NIGYKXG3NITgYCE0oTWhpD1LiBp5RuKGjQVdbf6Obre9AfT+lfZmBwAk7Yr8dPDUNHW1BhBl1yBn1xT3fRTUXNSxtZ9H+yJOdUIov6yXJcXh4oEMs7wXuvZqv8lUjISUEuaYOypL5iO7lGV3E/V3hjAoRAFS6tmy8yMxF1m5KsYLewQMIGApc+47mIgnGQ6u0ph6Q0CFEjSznSNkpyX81cHFQ4loWSLy+lbJSN0PdUmEM9JHMUdImgp0E14bTkYuSfuu6KgLzwGw8AAA==` +type SplashImage enum.Member[string] + +var ( + + // Gopher Eyes + GopherEyes = SplashImage{`H4sIAJB76FoCA9WWTRKDIAxG91zBjUcQSBTGo/QM3n/bbvozfFRCRGp3nW+q5PEwZLhRWHm17LdxHG4Ut+GZJIHz9oeJtUu3xDGXk/NIGYKXG3NITgYCE0oTWhpD1LiBp5RuKGjQVdbf6Obre9AfT+lfZmBwAk7Yr8dPDUNHW1BhBl1yBn1xT3fRTUXNSxtZ9H+yJOdUIov6yXJcXh4oEMs7wXuvZqv8lUjISUEuaYOypL5iO7lGV3E/V3hjAoRAFS6tmy8yMxF1m5KsYLewQMIGApc+47mIgnGQ6u0ph6Q0CFEjSznSNkpyX81cHFQ4loWSLy+lbJSN0PdUmEM9JHMUdImgp0E14bTkYuSfuu6KgLzwGw8AAA==`} -// Drawn in GIMP. Just a white grid on black background. -// const image = `H4sIAK/e01kCA5OONrGwNrU2MjbMVVCQjjaxzJUeFaFYhAunIkMzQgKjekaDkt5BOSoymtlHM/toZh8VIZjZAT4BWy4xCQAA` + // A simple white grid on a black background + WhiteGrid = SplashImage{`H4sIAK/e01kCA5OONrGwNrU2MjbMVVCQjjaxzJUeFaFYhAunIkMzQgKjekaDkt5BOSoymtlHM/toZh8VIZjZAT4BWy4xCQAA`} -// Blue and green circles. Drawn in Inkscape -// const image = `H4sIAKy5IlUCA91XSQ6DMAy85wu95Ak4K4in9A38/1qpakMb2yQxSYXKjRGKZ8ZLjNb5c7u7efWrsXF7vizbCwlTDsQSYOzyRlAcrTQXG0UC6IMYO3N8FMMFQtEGFMVJuO3HENQS/8ArYhO3SzhW9pGthPhSiiXSeAKxQp7fD1PsZzAhogYTdTlRXzQAkS4D2FdZ9RLt9ZVZPmmdoiWTFecXYbIIIRJKSa2wuqJdLd0f5XknTOOU2xhhkI2hQ/VeBaBGgeNTmebA0lDpP6jrKKzreVxd97pdcWFjubjU0RAGsP85lu2gsZyiqbarNQzcE2r67syi4JvUyJc7vMsRV9PZ7e7Ka/DB7tn9h+ABr+hfa40MAAA=` + // From a photo of Algernon Charles Swinburne, the poet + AlgernonPoet = SplashImage{`H4sICLDeClUCA2FsZ2Vybm9uLmFuc2kA1Vi5ccQwDMzZwiVXAgkCBDVXytVw/aeObM8IkLCCqcChdsQPi8X3ePN8yYuYPs/n483b53GM9B0yg+9WKUSI96tIqkH2q9Tssu1PYjH/SPgi4gb8M8z9zCrpAPJzn/INdXNFCKEUwgaZgBNobI6+AcgA7jOA+4zQWdqckfv8utwZFQo8S2+jC/Bnqxzrz5ZkB0FeoYB2KKEdtVQw4CsaH+7QxSkqehxsREK/9MjRlE56who2FMuGyAI5HUEo9dKYC0f/CDkNCGtAeOzIqhEiepw9yslRIyNSqwqbkq1/O6viJGiYaDndaAZxkrT8DSmHNYKVklNGMPAKhJsR7mwzYMwEwlUHSrcW3mUZDw3KiDNhdmxnQGxxcFrDjEUYQJbpYabq4YbXBidx29ZJdQEL9uFNVttvNQ9x2csJxBNwBTLjvNIR/SPkxGLlOB0oYFWOE+pEohkBnQPdZp9+f+BB2CknBRkD4b4BJouHB94ABEgtkghHUL6mJSHhIlJOytMazJSSxR8D/TTUVAL6d87ShLazyklSgVT4FahqJSSwA31Lan7lVNTzeiXsSAuKzYtyhfdSzYyigAGuYzFn556aN9SwT0lxcWdpa7hoqRbbmadtmTYcilCS2QcZ65ok5KmAUuxc+qd8AZBU6SmjGAAA`} -// From a photo of Algernon Charles Swinburne, the poet -// const image = `H4sICLDeClUCA2FsZ2Vybm9uLmFuc2kA1Vi5ccQwDMzZwiVXAgkCBDVXytVw/aeObM8IkLCCqcChdsQPi8X3ePN8yYuYPs/n483b53GM9B0yg+9WKUSI96tIqkH2q9Tssu1PYjH/SPgi4gb8M8z9zCrpAPJzn/INdXNFCKEUwgaZgBNobI6+AcgA7jOA+4zQWdqckfv8utwZFQo8S2+jC/Bnqxzrz5ZkB0FeoYB2KKEdtVQw4CsaH+7QxSkqehxsREK/9MjRlE56who2FMuGyAI5HUEo9dKYC0f/CDkNCGtAeOzIqhEiepw9yslRIyNSqwqbkq1/O6viJGiYaDndaAZxkrT8DSmHNYKVklNGMPAKhJsR7mwzYMwEwlUHSrcW3mUZDw3KiDNhdmxnQGxxcFrDjEUYQJbpYabq4YbXBidx29ZJdQEL9uFNVttvNQ9x2csJxBNwBTLjvNIR/SPkxGLlOB0oYFWOE+pEohkBnQPdZp9+f+BB2CknBRkD4b4BJouHB94ABEgtkghHUL6mJSHhIlJOytMazJSSxR8D/TTUVAL6d87ShLazyklSgVT4FahqJSSwA31Lan7lVNTzeiXsSAuKzYtyhfdSzYyigAGuYzFn556aN9SwT0lxcWdpa7hoqRbbmadtmTYcilCS2QcZ65ok5KmAUuxc+qd8AZBU6SmjGAAA` + splashImages = enum.New(GopherEyes, WhiteGrid, BlueGreenCircles, AlgernonPoet) + + // Select a random splash/banner image every time + splashImage *SplashImage = splashImages.Choice(0) + + // Select the gopher eyes every time + //splashImage = splashImages.GopherEyes +) // Decompress text that has first been gzipped and then base64 encoded func decompressImage(asciigfx string) string { @@ -76,7 +88,7 @@ func insertText(s, tabs string, linenr, offset int, message string, removal int) // Banner returns ANSI graphics with the current version number embedded in the text func Banner(versionString, description string) string { tabs := "\t\t\t\t" - s := tabs + strings.ReplaceAll("\n"+decompressImage(image), "\n", "\n"+tabs) + s := tabs + strings.ReplaceAll("\n"+decompressImage(splashImage.Value), "\n", "\n"+tabs) parts := strings.Fields(versionString) // See https://github.com/shiena/ansicolor/blob/master/README.md for ANSI color code table diff --git a/vendor/github.com/orsinium-labs/enum/.golangci.yaml b/vendor/github.com/orsinium-labs/enum/.golangci.yaml new file mode 100644 index 000000000..de5dcf82f --- /dev/null +++ b/vendor/github.com/orsinium-labs/enum/.golangci.yaml @@ -0,0 +1,18 @@ +linters: + disable: + - dupword + - depguard + presets: + - bugs + - comment + - complexity + # - error + - format + - import + # - metalinter + # - module + - performance + # - sql + # - style + # - test + - unused diff --git a/vendor/github.com/orsinium-labs/enum/.markdownlint.yaml b/vendor/github.com/orsinium-labs/enum/.markdownlint.yaml new file mode 100644 index 000000000..5f391c2a4 --- /dev/null +++ b/vendor/github.com/orsinium-labs/enum/.markdownlint.yaml @@ -0,0 +1,18 @@ +# https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml +default: true # enable all by default +MD007: # unordered list indentation + indent: 2 +MD013: false # do not validate line length +MD014: false # allow $ before command output +MD029: # ordered list prefix, use all ones + style: "one" +MD033: # allow html tags + allowed_elements: + - b + - div + - h1 + - img + - p + - picture + - source +MD041: false # the first line in readme is not a MD header diff --git a/vendor/github.com/orsinium-labs/enum/LICENSE b/vendor/github.com/orsinium-labs/enum/LICENSE new file mode 100644 index 000000000..211ae149e --- /dev/null +++ b/vendor/github.com/orsinium-labs/enum/LICENSE @@ -0,0 +1,21 @@ +MIT License + + 2023 Gram + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/orsinium-labs/enum/README.md b/vendor/github.com/orsinium-labs/enum/README.md new file mode 100644 index 000000000..568681683 --- /dev/null +++ b/vendor/github.com/orsinium-labs/enum/README.md @@ -0,0 +1,133 @@ +# enum + +[ [📄 docs](https://pkg.go.dev/github.com/orsinium-labs/enum) ] [ [🐙 github](https://github.com/orsinium-labs/enum) ] [ [❤️ sponsor](https://github.com/sponsors/orsinium) ] + +Type safe enums for Go without code generation or reflection. + +😎 Features: + +* Type-safe, thanks to generics. +* No code generation. +* No reflection. +* Well-documented, with working examples for every function. +* Flexible, supports both static and runtime definitions. +* Zero-dependency. + +## 📦 Installation + +```bash +go get github.com/orsinium-labs/enum +``` + +## 🛠️ Usage + +Define: + +```go +type Color enum.Member[string] + +var ( + Red = Color{"red"} + Green = Color{"green"} + Blue = Color{"blue"} + Colors = enum.New(Red, Green, Blue) +) +``` + +Parse a raw value (`nil` is returned for invalid value): + +```go +parsed := Colors.Parse("red") +``` + +Compare enum members: + +```go +parsed == Red +Red != Green +``` + +Accept enum members as function arguments: + +```go +func SetPixel(x, i int, c Color) +``` + +Loop over all enum members: + +```go +for _, color := range Colors.Members() { + // ... +} +``` + +Ensure that the enum member belongs to an enum (can be useful for defensive programming to ensure that the caller doesn't construct an enum member manually): + +```go +func f(color Color) { + if !colors.Contains(color) { + panic("invalid color") + } + // ... +} +``` + +Define custom methods on enum members: + +```go +func (c Color) UnmarshalJSON(b []byte) error { + return nil +} +``` + +Dynamically create enums to pass multiple members in a function: + +```go +func SetPixel2(x, y int, colors enum.Enum[Color, string]) { + if colors.Contains(Red) { + // ... + } +} + +purple := enum.New(Red, Blue) +SetPixel2(0, 0, purple) +``` + +Enum members can be any comparable type, not just strings: + +```go +type ColorValue struct { + UI string + DB int +} +type Color enum.Member[ColorValue] +var ( + Red = Color{ColorValue{"red", 1}} + Green = Color{ColorValue{"green", 2}} + Blue = Color{ColorValue{"blue", 3}} + Colors = enum.New(Red, Green, Blue) +) + +fmt.Println(Red.Value.UI) +``` + +If the enum has lots of members and new ones may be added over time, it's easy to forget to register all members in the enum. To prevent this, use enum.Builder to define an enum: + +```go +type Color enum.Member[string] + +var ( + b = enum.NewBuilder[string, Color]() + Red = b.Add(Color{"red"}) + Green = b.Add(Color{"green"}) + Blue = b.Add(Color{"blue"}) + Colors = b.Enum() +) +``` + +## 🤔 QnA + +1. **What happens when enums are added in Go itself?** I'll keep it alive until someone uses it but I expect the project popularity to quickly die out when there is native language support for enums. When you can mess with the compiler itself, you can do more. For example, this package can't provide an exhaustiveness check for switch statements using enums (maybe only by implementing a linter) but proper language-level enums would most likely have it. +1. **Is it reliable?** Yes, pretty much. It has good tests but most importantly it's a small project with just a bit of the actual code that is hard to mess up. +1. **Is it maintained?** The project is pretty much feature-complete, so there is nothing for me to commit and release daily. However, I accept contributions (see below). +1. **What if I found a bug?** Fork the project, fix the bug, write some tests, and open a Pull Request. I usually merge and release any contributions within a day. diff --git a/vendor/github.com/orsinium-labs/enum/Taskfile.yaml b/vendor/github.com/orsinium-labs/enum/Taskfile.yaml new file mode 100644 index 000000000..3377fb31d --- /dev/null +++ b/vendor/github.com/orsinium-labs/enum/Taskfile.yaml @@ -0,0 +1,18 @@ +# https://taskfile.dev +version: "3" + +tasks: + test: + desc: Run go tests with coverage and timeout and without cache + cmds: + - go test -count 1 -cover -timeout 1s ./... + + release: + desc: Tag and upload release + cmds: + - which gh + - test v{{.CLI_ARGS}} + - git tag v{{.CLI_ARGS}} + - git push + - git push --tags + - gh release create --generate-notes v{{.CLI_ARGS}} diff --git a/vendor/github.com/orsinium-labs/enum/enum.go b/vendor/github.com/orsinium-labs/enum/enum.go new file mode 100644 index 000000000..79413ff29 --- /dev/null +++ b/vendor/github.com/orsinium-labs/enum/enum.go @@ -0,0 +1,183 @@ +package enum + +import ( + "fmt" + "math/rand" + "strings" + "time" +) + +// Member is an enum member, a specific value bound to a variable. +type Member[T comparable] struct { + Value T +} + +// iMember is the type constraint for Member used by Enum. +// +// We can't use Member directly in type constraints +// because the users create a new subtype from Member +// instead of using it directly. +// +// We also can't use a normal interface because new types +// don't inherit methods of their base type. +type iMember[T comparable] interface { + ~struct{ Value T } +} + +// Enum is a collection of enum members. +// +// Use [New] to construct a new Enum from a list of members. +type Enum[M iMember[V], V comparable] struct { + members []M + v2m map[V]*M +} + +// New constructs a new [Enum] wrapping the given enum members. +func New[V comparable, M iMember[V]](members ...M) Enum[M, V] { + e := Enum[M, V]{members, nil} + e.v2m = make(map[V]*M) + for i, m := range e.members { + v := e.Value(m) + e.v2m[v] = &e.members[i] + } + return e +} + +// TypeName is a string representation of the wrapped type. +func (Enum[M, V]) TypeName() string { + return fmt.Sprintf("%T", *new(V)) +} + +// Empty returns true if the enum doesn't have any members. +func (e Enum[M, V]) Empty() bool { + return len(e.members) == 0 +} + +// Len returns how many members the enum has. +func (e Enum[M, V]) Len() int { + return len(e.members) +} + +// Contains returns true if the enum has the given member. +func (e Enum[M, V]) Contains(member M) bool { + for _, m := range e.members { + if m == member { + return true + } + } + return false +} + +// Parse converts a raw value into a member of the enum. +// +// If none of the enum members has the given value, nil is returned. +func (e Enum[M, V]) Parse(value V) *M { + return e.v2m[value] +} + +// Value returns the wrapped value of the given enum member. +func (e Enum[M, V]) Value(member M) V { + return Member[V](member).Value +} + +// Index returns the index of the given member in the enum. +// +// If the given member is not in the enum, it panics. +// Use [Enum.Contains] first if you don't know for sure +// if the member belongs to the enum. +func (e Enum[M, V]) Index(member M) int { + for i, m := range e.members { + if e.Value(m) == e.Value(member) { + return i + } + } + panic("the given Member does not belong to this Enum") +} + +// Members returns a slice of the members in the enum. +func (e Enum[M, V]) Members() []M { + return e.members +} + +// Choice returns a randomly selected member of the enum. +// +// A random seed can be given (or be 0 to use time.Now().UnixNano() as the seed). +// nil is returned only if the Enum contains no members. +func (e Enum[M, V]) Choice(seed int64) *M { + lenMembers := len(e.members) + // Enum is empty + if lenMembers == 0 { + return nil + } + if seed == 0 { + seed = time.Now().UnixNano() + } + // nolint: gosec + r := rand.New(rand.NewSource(seed)) + return &(e.members[r.Intn(lenMembers)]) +} + +// Values returns a slice of values of all members of the enum. +func (e Enum[M, V]) Values() []V { + res := make([]V, 0, len(e.members)) + for _, m := range e.members { + res = append(res, e.Value(m)) + } + return res +} + +// String implements [fmt.Stringer] interface. +// +// It returns a comma-separated list of values of the enum members. +func (e Enum[M, V]) String() string { + values := make([]string, 0, len(e.members)) + for _, m := range e.members { + values = append(values, fmt.Sprintf("%v", e.Value(m))) + } + return strings.Join(values, ", ") +} + +// GoString implements [fmt.GoStringer] interface. +// +// When you print a member using "%#v" format, +// it will show the enum representation as a valid Go syntax. +func (e Enum[M, V]) GoString() string { + values := make([]string, 0, len(e.members)) + for _, m := range e.members { + values = append(values, fmt.Sprintf("%T{%#v}", m, e.Value(m))) + } + joined := strings.Join(values, ", ") + return fmt.Sprintf("enum.New(%s)", joined) +} + +// Builder is a constructor for an [Enum]. +// +// Use [Builder.Add] to add new members to the future enum +// and then call [Builder.Enum] to create a new [Enum] with all added members. +// +// Builder is useful for when you have lots of enum members, and new ones +// are added over time, as the project grows. In such scenario, it's easy to forget +// to add in the [Enum] a newly created [Member]. +// The builder is designed to prevent that. +type Builder[M iMember[V], V comparable] struct { + members []M + finished bool +} + +// NewBuilder creates a new [Builder], a constructor for an [Enum]. +func NewBuilder[V comparable, M iMember[V]]() Builder[M, V] { + return Builder[M, V]{make([]M, 0), false} +} + +// Add registers a new [Member] in the builder. +func (b *Builder[M, V]) Add(m M) M { + b.members = append(b.members, m) + return m +} + +// Enum creates a new [Enum] with all members registered using [Builder.Add]. +func (b *Builder[M, V]) Enum() Enum[M, V] { + b.finished = true + e := New(b.members...) + return e +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 2df75a4d1..f3e4af746 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -162,6 +162,9 @@ github.com/onsi/ginkgo/v2/internal/interrupt_handler github.com/onsi/ginkgo/v2/internal/parallel_support github.com/onsi/ginkgo/v2/reporters github.com/onsi/ginkgo/v2/types +# github.com/orsinium-labs/enum v1.3.0 +## explicit; go 1.20 +github.com/orsinium-labs/enum # github.com/patrickmn/go-cache v2.1.0+incompatible ## explicit github.com/patrickmn/go-cache