go-generror

command module
v0.0.0-...-1c8ba67 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2019 License: MIT Imports: 6 Imported by: 0

README

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.

Documentation

Overview

```go

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

```

with `go generate` command

```go

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

```

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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