protoenum provides utilities to manage Protobuf enum metadata in Go. It wraps Protobuf enum values with custom descriptions and offers enum collections with simple lookup by code, name, or description.
🎯 Smart Enum Management: Wrap Protobuf enums with custom descriptions and metadata ⚡ Multi-Lookup Support: Fast lookup by code, name, or description with strict validation 🔄 Type-Safe Operations: Preserve protobuf type safe operations with enhanced metadata 🛡️ Strict Design: Single usage pattern prevents misuse with required defaults 🌍 Production Grade: Battle-tested enum handling in enterprise applications
go get github.com/go-xlan/protoenumThe project includes example proto files:
protoenumstatus.proto- Basic status enumprotoenumresult.proto- Test result enum
package main
import (
"github.com/go-xlan/protoenum"
"github.com/go-xlan/protoenum/protos/protoenumstatus"
"github.com/yyle88/zaplog"
"go.uber.org/zap"
)
// Build status enum collection
var enums = protoenum.NewEnums(
protoenum.NewEnum(protoenumstatus.StatusEnum_UNKNOWN, "未知"),
protoenum.NewEnum(protoenumstatus.StatusEnum_SUCCESS, "成功"),
protoenum.NewEnum(protoenumstatus.StatusEnum_FAILURE, "失败"),
)
func main() {
// Get enhanced description from protobuf enum (returns default when not found)
item := enums.GetByCode(int32(protoenumstatus.StatusEnum_SUCCESS))
zaplog.LOG.Debug("desc", zap.String("msg", item.Desc()))
// Convert between protoenum and native enum (safe with default fallback)
enum := enums.GetByName("SUCCESS")
base := protoenumstatus.StatusEnum(enum.Code())
zaplog.LOG.Debug("base", zap.String("msg", base.String()))
// Use in business logic
if base == protoenumstatus.StatusEnum_SUCCESS {
zaplog.LOG.Debug("done")
}
}⬆️ Source: Source
package main
import (
"github.com/go-xlan/protoenum"
"github.com/go-xlan/protoenum/protos/protoenumresult"
"github.com/yyle88/zaplog"
"go.uber.org/zap"
)
// Build enum collection
var enums = protoenum.NewEnums(
protoenum.NewEnum(protoenumresult.ResultEnum_UNKNOWN, "其它"),
protoenum.NewEnum(protoenumresult.ResultEnum_PASS, "通过"),
protoenum.NewEnum(protoenumresult.ResultEnum_FAIL, "出错"),
protoenum.NewEnum(protoenumresult.ResultEnum_SKIP, "跳过"),
)
func main() {
// Lookup by enum code (returns default when not found)
skip := enums.GetByCode(int32(protoenumresult.ResultEnum_SKIP))
zaplog.LOG.Debug("desc", zap.String("msg", skip.Desc()))
// Lookup by enum name (safe with default fallback)
pass := enums.GetByName("PASS")
base := protoenumresult.ResultEnum(pass.Code())
zaplog.LOG.Debug("base", zap.String("msg", base.String()))
// Business logic with native enum
if base == protoenumresult.ResultEnum_PASS {
zaplog.LOG.Debug("pass")
}
// Lookup by Chinese description (returns default when not found)
skip = enums.GetByDesc("跳过")
zaplog.LOG.Debug("name", zap.String("msg", skip.Name()))
}⬆️ Source: Source
| Method | Description | Returns |
|---|---|---|
NewEnum(value, desc) |
Create enum instance | *Enum[T] |
enum.Base() |
Get underlying protobuf enum | T |
enum.Code() |
Get numeric code | int32 |
enum.Name() |
Get enum name | string |
enum.Desc() |
Get description | string |
enum.Hans() |
Get Chinese description (alias to Desc) | string |
| Method | Description | Returns |
|---|---|---|
NewEnums(items...) |
Create collection with strict validation (first item becomes default) | *Enums[T] |
enums.GetByEnum(enum) |
Lookup by protobuf enum (returns default if not found, panics if no default) | *Enum[T] |
enums.GetByCode(code) |
Lookup by code (returns default if not found, panics if no default) | *Enum[T] |
enums.GetByName(name) |
Lookup by name (returns default if not found, panics if no default) | *Enum[T] |
enums.GetByDesc(desc) |
Lookup by description (returns default if not found, panics if no default) | *Enum[T] |
enums.GetByHans(hans) |
Lookup by Chinese description (alias to GetByDesc) | *Enum[T] |
enums.MustGetByEnum(enum) |
Strict lookup by protobuf enum (panics if not found) | *Enum[T] |
enums.MustGetByCode(code) |
Strict lookup by code (panics if not found) | *Enum[T] |
enums.MustGetByName(name) |
Strict lookup by name (panics if not found) | *Enum[T] |
enums.MustGetByDesc(desc) |
Strict lookup by description (panics if not found) | *Enum[T] |
enums.MustGetByHans(hans) |
Strict lookup by Chinese description (alias to MustGetByDesc) | *Enum[T] |
enums.GetDefault() |
Get current default value (panics if unset) | *Enum[T] |
enums.SetDefault(enum) |
Set default (requires no existing default) | void |
enums.UnsetDefault() |
Remove default (requires existing default) | void |
enums.WithDefaultEnum(enum) |
Chain: set default by enum instance | *Enums[T] |
enums.WithDefaultCode(code) |
Chain: set default by code (panics if not found) | *Enums[T] |
enums.WithDefaultName(name) |
Chain: set default by name (panics if not found) | *Enums[T] |
enums.WithUnsetDefault() |
Chain: remove default value | *Enums[T] |
Creating enhanced enum instance:
statusEnum := protoenum.NewEnum(protoenumstatus.StatusEnum_SUCCESS, "操作成功")
fmt.Printf("Code: %d, Name: %s, Description: %s\n",
statusEnum.Code(), statusEnum.Name(), statusEnum.Desc())Accessing underlying protobuf enum:
originalEnum := statusEnum.Base()
if originalEnum == protoenumstatus.StatusEnum_SUCCESS {
fmt.Println("Success status detected")
}Building enum collections:
statusEnums := protoenum.NewEnums(
protoenum.NewEnum(protoenumstatus.StatusEnum_UNKNOWN, "未知状态"),
protoenum.NewEnum(protoenumstatus.StatusEnum_SUCCESS, "成功"),
protoenum.NewEnum(protoenumstatus.StatusEnum_FAILURE, "失败"),
)Multiple lookup methods:
// By numeric code - always returns valid enum (default if not found)
enum := statusEnums.GetByCode(1)
fmt.Printf("Found: %s\n", enum.Desc())
// By enum name - guaranteed non-nil
enum = statusEnums.GetByName("SUCCESS")
fmt.Printf("Status: %s\n", enum.Desc())
// By Chinese description - safe with default fallback
enum = statusEnums.GetByDesc("成功")
fmt.Printf("Code: %d\n", enum.Code())
// Strict lookup - panics if not found (no default fallback)
enum = statusEnums.MustGetByCode(1)
fmt.Printf("Strict: %s\n", enum.Desc())Type conversion patterns:
// Convert from enum instance to native protobuf enum
// Always returns valid enum (with default fallback)
statusEnum := enums.GetByName("SUCCESS")
native := protoenumstatus.StatusEnum(statusEnum.Code())
// Use native enum in protobuf operations with safe accessStrict validation patterns:
// Use MustGetByXxx with strict validation (panics if not found)
result := enums.MustGetByDesc("成功") // Panics if description not in collection
fmt.Printf("Found: %s\n", result.Name())
// GetByXxx returns default on unknown values
result = enums.GetByDesc("不存在的描述") // Returns default (UNKNOWN)
fmt.Printf("Fallback: %s\n", result.Name())Automatic default value (first item):
enums := protoenum.NewEnums(
protoenum.NewEnum(protoenumstatus.StatusEnum_UNKNOWN, "未知"),
protoenum.NewEnum(protoenumstatus.StatusEnum_SUCCESS, "成功"),
)
// First item (UNKNOWN) becomes default on creation
defaultEnum := enums.GetDefault()Strict default management:
// Collections MUST have a default value
// NewEnums sets first item as default on init
enums := protoenum.NewEnums(
protoenum.NewEnum(protoenumstatus.StatusEnum_UNKNOWN, "未知"),
protoenum.NewEnum(protoenumstatus.StatusEnum_SUCCESS, "成功"),
)
// Lookup failures return default (not nil)
notFound := enums.GetByCode(999) // Returns UNKNOWN (default)
fmt.Printf("Fallback: %s\n", notFound.Desc()) // Safe without nil check
// Change default using strict pattern
enums.UnsetDefault() // Must unset first
enums.SetDefault(enums.MustGetByCode(1)) // Then set new default
// After UnsetDefault, lookups panic if not found
// This enforces single usage pattern: collections must have defaultsMIT License. See LICENSE.
Contributions are welcome! Report bugs, suggest features, and contribute code:
- 🐛 Found a bug? Open an issue on GitHub with reproduction steps
- 💡 Have a feature idea? Create an issue to discuss the suggestion
- 📖 Documentation confusing? Report it so we can improve
- 🚀 Need new features? Share the use cases to help us understand requirements
- ⚡ Performance issue? Help us optimize through reporting slow operations
- 🔧 Configuration problem? Ask questions about complex setups
- 📢 Follow project progress? Watch the repo to get new releases and features
- 🌟 Success stories? Share how this package improved the workflow
- 💬 Feedback? We welcome suggestions and comments
New code contributions, follow this process:
- Fork: Fork the repo on GitHub (using the webpage UI).
- Clone: Clone the forked project (
git clone https://github.com/yourname/repo-name.git). - Navigate: Navigate to the cloned project (
cd repo-name) - Branch: Create a feature branch (
git checkout -b feature/xxx). - Code: Implement the changes with comprehensive tests
- Testing: (Golang project) Ensure tests pass (
go test ./...) and follow Go code style conventions - Documentation: Update documentation to support client-facing changes and use significant commit messages
- Stage: Stage changes (
git add .) - Commit: Commit changes (
git commit -m "Add feature xxx") ensuring backward compatible code - Push: Push to the branch (
git push origin feature/xxx). - PR: Open a pull request on GitHub (on the GitHub webpage) with detailed description.
Please ensure tests pass and include relevant documentation updates.
Welcome to contribute to this project via submitting merge requests and reporting issues.
Project Support:
- ⭐ Give GitHub stars if this project helps you
- 🤝 Share with teammates and (golang) programming friends
- 📝 Write tech blogs about development tools and workflows - we provide content writing support
- 🌟 Join the ecosystem - committed to supporting open source and the (golang) development scene
Have Fun Coding with this package! 🎉