errnumer
A go:generate tool for usage with ProtoNATS.

Overview
Errnumer is a code generation tool that simplifies error handling in Go applications using ProtoNATS.
It generates error enumerations and associated helper functions, so you can define your errors in a shared package and have it used both in client and server side.
Usage
Create a go file (e.g., errors.go, preferably in your shared Protobuf generated Go package) and add the following go:generate directive:
type errnumerAtlasError struct {
Code string
Description string
}
var (
errnumerAtlasErrorUnknown = errnumerAtlasError{"500", "An unknown or unhandled server error occurred."}
errnumerAtlasErrorPlayerNotFound = errnumerAtlasError{"1001", "The specified player could not be found."}
errnumerAtlasErrorPlayerAlreadyOnline = errnumerAtlasError{"1002", "A session for this player already exists."}
errnumerAtlasErrorSessionNotFound = errnumerAtlasError{"1101", "The specified session could not be found."}
errnumerAtlasErrorActiveSession = errnumerAtlasError{"1102", "The current player already has an active session."}
)
Currently it's a bit repetitive to define the error struct, so I might change it in the future, but it's what I got for the moment.
All of this should be declared unexported, such that only the generated code is exported.
The code generated would look like this:
package atlas
import "xiam.li/protonats/go/protonats"
type AtlasError int
const (
AtlasErrorUnknown AtlasError = iota
AtlasErrorPlayerNotFound
AtlasErrorPlayerAlreadyOnline
AtlasErrorSessionNotFound
AtlasErrorActiveSession
)
var AtlasErrorCodes = []string{
"500",
"1001",
"1002",
"1101",
"1102",
}
var AtlasErrorDescriptions = []string{
"An unknown or unhandled server error occurred.",
"The specified player could not be found.",
"A session for this player already exists.",
"The specified session could not be found.",
"The current player already has an active session.",
}
func (e AtlasError) AsServerError() protonats.ServerError {
codeString := AtlasErrorCodes[e]
description := AtlasErrorDescriptions[e]
return protonats.NewServerErr(codeString, description)
}
func (e AtlasError) String() string {
if e < 0 || int(e) >= len(AtlasErrorDescriptions) {
return AtlasErrorDescriptions[0]
}
return AtlasErrorDescriptions[e]
}
func (e AtlasError) Code() string {
if e < 0 || int(e) >= len(AtlasErrorCodes) {
return AtlasErrorCodes[0]
}
return AtlasErrorCodes[e]
}
func AtlasErrorValues() []AtlasError {
return []AtlasError{
AtlasErrorUnknown,
AtlasErrorPlayerNotFound,
AtlasErrorPlayerAlreadyOnline,
AtlasErrorSessionNotFound,
AtlasErrorActiveSession,
}
}
func AtlasErrorFromCode(s string) (AtlasError, bool) {
for i, code := range AtlasErrorCodes {
if code == s {
return AtlasError(i), true
}
}
return AtlasError(0), false
}
func AtlasErrorFrom(err error) (AtlasError, bool) {
if srvErr, ok := protonats.AsServiceError(err); ok {
return AtlasErrorFromCode(srvErr.Code)
}
return AtlasError(0), false
}
On the client side, this could be used like this:
response, err := atlasClient.Login( )
if err != nil {
if atlasErr, ok := atlas.AtlasErrorFrom(err); ok {
fmt.Printf("Atlas service error: %s (code %s)\n", atlasErr.String(), atlasErr.Code())
switch atlasErr {
case atlas.AtlasErrorActiveSession:
fallthrough
case atlas.AtlasErrorPlayerAlreadyOnline:
return false, sessionAlreadyExistsComponent
default:
fmt.Printf("Got a valid, but unexpected Atlas error: %s\n", atlasErr.String())
return false, sessionServersUnavailableComponent
}
}
return false, sessionServersUnavailableComponent
}
Or you can do it manually like this:
code, ok := AtlasErrorFromCode("1002")
if !ok {
fmt.Print("Not a valid Atlas error code")
return
}
switch code {
case AtlasErrorPlayerNotFound:
fmt.Print("Player is not found")
case AtlasErrorPlayerAlreadyOnline:
fmt.Print("Player is already online")
default:
fmt.Printf("Got a valid, but unexpected error: %s", code.String())
}
On the server side, you would use it like this:
if hasActiveSession {
return nil, atlas.AtlasErrorActiveSession.AsServerError()
}
License
MIT © 2025 Dorian Heinrichs