Skip to content
master
Switch branches/tags
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 
 
 
 
 

go-generror

Error generator for Go.

Usage

    //errcode {DetailCode}[,paramName paramType]...

with go generate command

    //go:generate go-generror [Code]...

generated Error usage sample is following

type NameSpec struct {
    lessThan int
    moreThan int
}

func (s NameSpec) Validate(name string) Error {
    //errcode NameIsInvalidLength,lessThan int,moreThan int
    if len(name) >= s.lessThan || len(name) <= s.moreThan {
        return ErrorBadRequest(errors.New("invalid name"), NameIsInvalidLengthError(s.lessThan, s.moreThan))
    }
    return nil
}

and

func(w http.ResponseWriter, r *http.Request) {
    nameSpec := NameSpec{
        lessThan: 100,
        moreThan: 0,
    }
    err := nameSpec.Validate(r.Query().Get("name"))
    if err != nil {
        renderError(w, err)
        return
    }
    // ...
}

func renderError(w http.ResponseWriter, err Error) {
    switch {
        case err.IsUnknown():
            // ...
        case err.IsBadRequst():
            // ...
        // ...
    }
    // ...
}

Example

def ./_example/example.go

generated ./_example/error_gen.go

Installation

$ go get github.com/hori-ryota/go-generror

Extend output

We can define multi renderers. Like following

templates := []template.Template{
    generror.GodefTmpl,
    customTmpl1,
    customTmpl2,
}

renderers := make([]func(generror.TemplParam), len(templates))
for i := range templates {
    tmpl := tempates[i]
    dstFileName := dstFileNames[i]
    renderers[i] = func(param generror.TmplParam) error {

        importPackages := map[string]string{
            "fmt":     "fmt",
            "strings": "strings",
            "zap":     "go.uber.org/zap",
            "zapcore": "go.uber.org/zap/zapcore",
            "zaperr":  "github.com/hori-ryota/zaperr",
        }
        for k, v := range param.ImportPackages {
            importPackages[k] = v
        }

        param.ImportPackages = importPackages

        buf := new(bytes.Buffer)
        err := tmpl.Execute(buf, param)
        if err != nil {
            return err
        }

        out, err := format.Source(buf.Bytes())
        if err != nil {
            return err
        }
        return ioutil.WriteFile(dstFileName, out, 0644)
    }
}

return generror.Run(".", args[1:], renderers)

This mean we can create formatter interfaces. e.g.

template def

type ErrorFormatter interface {
    {{- range .DetailErrorCodes }}
    {{ .Code }}Error(
        {{- range .Params }}
        {{ .Name }} {{ .Type }},
        {{- end }}
    ) string
    {{- end }}
}

type ErrorDetail struct {
    Code string
    Args []interface{}
}

func FormatError(formatter ErrorFormatter, err ErrorDetail) string {
    switch err.Code {
    {{- range .DetailErrorCodes }}
    case "{{ .Code }}":
        return formatter.{{ .Code }}Error(
            {{- range $i, $v := .Params }}
            err.Args[{{ $i }}].({{ $v.Type }}),
            {{- end }}
        )
    {{- end }}
    }
}

generated

type ErrorFormatter interface {
    NameIsInvalidLengthError(
        lessThan int,
        moreThan int,
    ) string
    FooError(
        arg1 string,
        arg2 time.Time,
        arg3 int,
    ) string
    BarError() string
}

type ErrorDetail struct {
    Code string
    Args []interface{}
}

func FormatError(formatter ErrorFormatter, err ErrorDetail) string {
    switch err.Code {
    case "NameIsInvalidLength":
        return formatter.NameIsInvalidLengthError(
            err.Args[0].(int),
            err.Args[1].(int),
        )
    case "Foo":
        return formatter.FooError(
            err.Args[0].(string),
            err.Args[1].(time.Time),
            err.Args[2].(int),
        )
    case "Bar":
        return formatter.BarError()
    }
}

We needs only implements ErrorFormatter interface with benefits of Type-safe. Let's multilingual support!

Of course, this automatic generation is not limited to Go language. Since it can be generated also in Kotlin language etc. by devising it, it should be able to provide interface to frontend without tedious documentation.

And, of course, we can also generate documentation automatically.

About

No description, website, or topics provided.

Resources

License

Releases

No releases published

Packages

No packages published

Languages