Compare commits
No commits in common. "generic" and "main" have entirely different histories.
10 changed files with 676 additions and 63 deletions
30
README.md
30
README.md
|
@ -1,7 +1,35 @@
|
||||||
# gotry
|
# gotry
|
||||||
|
|
||||||
This package is a fork of lainio's [err2](https://github.com/lainio/err2)
|
This package is a fork of lainio's [err2](https://github.com/lainio/err2)
|
||||||
package.
|
package with an improved code generator.
|
||||||
|
|
||||||
|
## Add custom try package to your project
|
||||||
|
|
||||||
|
```
|
||||||
|
go install code.thetadev.de/ThetaDev/gotry/gotry_generate
|
||||||
|
|
||||||
|
gotry_generate -init
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add type definitions
|
||||||
|
|
||||||
|
```
|
||||||
|
📂 try/types.csv
|
||||||
|
|
||||||
|
<Handler method name>;<Type>;<Import#1>;<Import#2>;...
|
||||||
|
|
||||||
|
# Example:
|
||||||
|
DB;*gorm.DB;gorm.io/gorm
|
||||||
|
|
||||||
|
# Add +s to the method name to generate 2 handlers for single values and slices
|
||||||
|
String+s;string
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generate
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go generate ./try
|
||||||
|
```
|
||||||
|
|
||||||
## Handle errors
|
## Handle errors
|
||||||
|
|
||||||
|
|
11
embed.go
Normal file
11
embed.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package gotry
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
|
//go:generate go run ./gotry_generate -of std_types
|
||||||
|
|
||||||
|
//go:embed try/*.go
|
||||||
|
var TryFiles embed.FS
|
||||||
|
|
||||||
|
//go:embed try/types.tmpl
|
||||||
|
var TypesTemplate string
|
2
go.mod
2
go.mod
|
@ -1,3 +1,3 @@
|
||||||
module code.thetadev.de/ThetaDev/gotry
|
module code.thetadev.de/ThetaDev/gotry
|
||||||
|
|
||||||
go 1.18
|
go 1.16
|
||||||
|
|
150
gotry_generate/main.go
Normal file
150
gotry_generate/main.go
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"code.thetadev.de/ThetaDev/gotry"
|
||||||
|
"code.thetadev.de/ThetaDev/gotry/try"
|
||||||
|
)
|
||||||
|
|
||||||
|
var variablePattern = regexp.MustCompile(`^(\w[\w\d]*)(\+s)?$`)
|
||||||
|
|
||||||
|
type line struct {
|
||||||
|
Name string
|
||||||
|
WithSlice bool
|
||||||
|
Types []string
|
||||||
|
UseExt bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDefinitions(defFileName, outFileName, pkgName string) (err try.Err) {
|
||||||
|
defer try.Annotate(&err, "error parsing definitions")
|
||||||
|
|
||||||
|
defFile := try.File(os.Open(defFileName))
|
||||||
|
useExt := pkgName != "try"
|
||||||
|
|
||||||
|
fileScanner := bufio.NewScanner(defFile)
|
||||||
|
fileScanner.Split(bufio.ScanLines)
|
||||||
|
|
||||||
|
ln := 0
|
||||||
|
var lines []line
|
||||||
|
imports := map[string]bool{}
|
||||||
|
|
||||||
|
for fileScanner.Scan() {
|
||||||
|
lineStr := strings.TrimSpace(fileScanner.Text())
|
||||||
|
ln++
|
||||||
|
|
||||||
|
if lineStr == "" || strings.HasPrefix(lineStr, "#") ||
|
||||||
|
strings.HasPrefix(lineStr, "/") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lineData, err := parseLine(lineStr, imports)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("[Err] line %d, %s: \"%s\n", ln, err.Error(), lineStr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lineData.UseExt = useExt
|
||||||
|
|
||||||
|
lines = append(lines, *lineData)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Parsed %d definitions\n", len(lines))
|
||||||
|
|
||||||
|
outFile := try.File(os.Create(outFileName))
|
||||||
|
|
||||||
|
tmpl := template.Must(template.New("templ").Parse(gotry.TypesTemplate))
|
||||||
|
|
||||||
|
try.Check(tmpl.Execute(outFile, struct {
|
||||||
|
Definitions []line
|
||||||
|
Imports map[string]bool
|
||||||
|
PkgName string
|
||||||
|
UseExt bool
|
||||||
|
}{
|
||||||
|
Definitions: lines,
|
||||||
|
Imports: imports,
|
||||||
|
PkgName: pkgName,
|
||||||
|
UseExt: useExt,
|
||||||
|
}))
|
||||||
|
|
||||||
|
try.Check(defFile.Close())
|
||||||
|
try.Check(outFile.Close())
|
||||||
|
|
||||||
|
try.Check(goFmt(outFileName))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLine(lineStr string, imports map[string]bool) (*line, try.Err) {
|
||||||
|
lineParts := strings.Split(lineStr, ";")
|
||||||
|
lineData := line{}
|
||||||
|
|
||||||
|
if len(lineParts) < 2 {
|
||||||
|
return nil, try.NewErr("missing type name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse variable name
|
||||||
|
variableRaw := strings.TrimSpace(lineParts[0])
|
||||||
|
if match := variablePattern.FindStringSubmatch(variableRaw); match != nil {
|
||||||
|
lineData.Name = match[1]
|
||||||
|
lineData.WithSlice = match[2] != ""
|
||||||
|
} else {
|
||||||
|
return nil, try.NewErr("invalid variable name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse type name(s)
|
||||||
|
for _, typeStr := range strings.Split(lineParts[1], ",") {
|
||||||
|
typeStr = strings.TrimSpace(typeStr)
|
||||||
|
if typeStr != "" {
|
||||||
|
lineData.Types = append(lineData.Types, typeStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(lineData.Types) == 0 {
|
||||||
|
return nil, try.NewErr("invalid type name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse imports
|
||||||
|
for i := 2; i < len(lineParts); i++ {
|
||||||
|
pkgName := strings.TrimSpace(lineParts[i])
|
||||||
|
imports[pkgName] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return &lineData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func goFmt(goFile string) error {
|
||||||
|
return exec.Command("gofmt", "-s", "-w", goFile).Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
defer try.CatchTrace(1)
|
||||||
|
|
||||||
|
// Read flags
|
||||||
|
var definitionFile string
|
||||||
|
var targetDir string
|
||||||
|
var typeFile string
|
||||||
|
|
||||||
|
flag.StringVar(&definitionFile, "def", path.Join("try", "types.csv"),
|
||||||
|
"Definition file")
|
||||||
|
flag.StringVar(&targetDir, "o", "try", "Target directory")
|
||||||
|
flag.StringVar(&typeFile, "of", "types", "Name of generated type definition file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
try.Check(os.MkdirAll(targetDir, 0o777))
|
||||||
|
|
||||||
|
// Generate types
|
||||||
|
if _, e := os.Stat(definitionFile); os.IsNotExist(e) {
|
||||||
|
try.Check(try.NewErr("Definition file does not exist"))
|
||||||
|
} else {
|
||||||
|
try.Check(
|
||||||
|
parseDefinitions(definitionFile, path.Join(targetDir, typeFile+".go"),
|
||||||
|
path.Base(targetDir)))
|
||||||
|
}
|
||||||
|
}
|
290
try/std_types.go
Normal file
290
try/std_types.go
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
package try
|
||||||
|
|
||||||
|
// gotry auto-generated type definitions. DO NOT EDIT.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bool is a helper method to handle errors of
|
||||||
|
// func() (bool, error) functions.
|
||||||
|
func Bool(v bool, err error) bool {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bools is a helper method to handle errors of
|
||||||
|
// func() ([]bool, error) functions.
|
||||||
|
func Bools(v []bool, err error) []bool {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Byte is a helper method to handle errors of
|
||||||
|
// func() (byte, error) functions.
|
||||||
|
func Byte(v byte, err error) byte {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes is a helper method to handle errors of
|
||||||
|
// func() ([]byte, error) functions.
|
||||||
|
func Bytes(v []byte, err error) []byte {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rune is a helper method to handle errors of
|
||||||
|
// func() (rune, error) functions.
|
||||||
|
func Rune(v rune, err error) rune {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runes is a helper method to handle errors of
|
||||||
|
// func() ([]rune, error) functions.
|
||||||
|
func Runes(v []rune, err error) []rune {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is a helper method to handle errors of
|
||||||
|
// func() (string, error) functions.
|
||||||
|
func String(v string, err error) string {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strings is a helper method to handle errors of
|
||||||
|
// func() ([]string, error) functions.
|
||||||
|
func Strings(v []string, err error) []string {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int is a helper method to handle errors of
|
||||||
|
// func() (int, error) functions.
|
||||||
|
func Int(v int, err error) int {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ints is a helper method to handle errors of
|
||||||
|
// func() ([]int, error) functions.
|
||||||
|
func Ints(v []int, err error) []int {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8 is a helper method to handle errors of
|
||||||
|
// func() (int8, error) functions.
|
||||||
|
func Int8(v int8, err error) int8 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8s is a helper method to handle errors of
|
||||||
|
// func() ([]int8, error) functions.
|
||||||
|
func Int8s(v []int8, err error) []int8 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int16 is a helper method to handle errors of
|
||||||
|
// func() (int16, error) functions.
|
||||||
|
func Int16(v int16, err error) int16 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int16s is a helper method to handle errors of
|
||||||
|
// func() ([]int16, error) functions.
|
||||||
|
func Int16s(v []int16, err error) []int16 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32 is a helper method to handle errors of
|
||||||
|
// func() (int32, error) functions.
|
||||||
|
func Int32(v int32, err error) int32 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32s is a helper method to handle errors of
|
||||||
|
// func() ([]int32, error) functions.
|
||||||
|
func Int32s(v []int32, err error) []int32 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 is a helper method to handle errors of
|
||||||
|
// func() (int64, error) functions.
|
||||||
|
func Int64(v int64, err error) int64 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64s is a helper method to handle errors of
|
||||||
|
// func() ([]int64, error) functions.
|
||||||
|
func Int64s(v []int64, err error) []int64 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint is a helper method to handle errors of
|
||||||
|
// func() (uint, error) functions.
|
||||||
|
func Uint(v uint, err error) uint {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uints is a helper method to handle errors of
|
||||||
|
// func() ([]uint, error) functions.
|
||||||
|
func Uints(v []uint, err error) []uint {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8 is a helper method to handle errors of
|
||||||
|
// func() (uint8, error) functions.
|
||||||
|
func Uint8(v uint8, err error) uint8 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8s is a helper method to handle errors of
|
||||||
|
// func() ([]uint8, error) functions.
|
||||||
|
func Uint8s(v []uint8, err error) []uint8 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16 is a helper method to handle errors of
|
||||||
|
// func() (uint16, error) functions.
|
||||||
|
func Uint16(v uint16, err error) uint16 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16s is a helper method to handle errors of
|
||||||
|
// func() ([]uint16, error) functions.
|
||||||
|
func Uint16s(v []uint16, err error) []uint16 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32 is a helper method to handle errors of
|
||||||
|
// func() (uint32, error) functions.
|
||||||
|
func Uint32(v uint32, err error) uint32 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32s is a helper method to handle errors of
|
||||||
|
// func() ([]uint32, error) functions.
|
||||||
|
func Uint32s(v []uint32, err error) []uint32 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 is a helper method to handle errors of
|
||||||
|
// func() (uint64, error) functions.
|
||||||
|
func Uint64(v uint64, err error) uint64 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64s is a helper method to handle errors of
|
||||||
|
// func() ([]uint64, error) functions.
|
||||||
|
func Uint64s(v []uint64, err error) []uint64 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32 is a helper method to handle errors of
|
||||||
|
// func() (float32, error) functions.
|
||||||
|
func Float32(v float32, err error) float32 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32s is a helper method to handle errors of
|
||||||
|
// func() ([]float32, error) functions.
|
||||||
|
func Float32s(v []float32, err error) []float32 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 is a helper method to handle errors of
|
||||||
|
// func() (float64, error) functions.
|
||||||
|
func Float64(v float64, err error) float64 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64s is a helper method to handle errors of
|
||||||
|
// func() ([]float64, error) functions.
|
||||||
|
func Float64s(v []float64, err error) []float64 {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrStr is a helper method to handle errors of
|
||||||
|
// func() (string, string, error) functions.
|
||||||
|
func StrStr(v string, v1 string, err error) (string, string) {
|
||||||
|
Check(err)
|
||||||
|
return v, v1
|
||||||
|
}
|
||||||
|
|
||||||
|
// X is a helper method to handle errors of
|
||||||
|
// func() (interface{}, error) functions.
|
||||||
|
func X(v interface{}, err error) interface{} {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// File is a helper method to handle errors of
|
||||||
|
// func() (*os.File, error) functions.
|
||||||
|
func File(v *os.File, err error) *os.File {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reader is a helper method to handle errors of
|
||||||
|
// func() (io.Reader, error) functions.
|
||||||
|
func Reader(v io.Reader, err error) io.Reader {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writer is a helper method to handle errors of
|
||||||
|
// func() (io.Writer, error) functions.
|
||||||
|
func Writer(v io.Writer, err error) io.Writer {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request is a helper method to handle errors of
|
||||||
|
// func() (*http.Request, error) functions.
|
||||||
|
func Request(v *http.Request, err error) *http.Request {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response is a helper method to handle errors of
|
||||||
|
// func() (*http.Response, error) functions.
|
||||||
|
func Response(v *http.Response, err error) *http.Response {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Url is a helper method to handle errors of
|
||||||
|
// func() (*url.URL, error) functions.
|
||||||
|
func Url(v *url.URL, err error) *url.URL {
|
||||||
|
Check(err)
|
||||||
|
return v
|
||||||
|
}
|
68
try/std_types_test.go
Normal file
68
try/std_types_test.go
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package try
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStrHelper_noThrow(t *testing.T) {
|
||||||
|
s := String(noThrow())
|
||||||
|
if s != "test" {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrHelper_throw(t *testing.T) {
|
||||||
|
var err Err
|
||||||
|
defer Return(&err)
|
||||||
|
|
||||||
|
String(throw())
|
||||||
|
|
||||||
|
t.Fail() // If everything works we are never here
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrStrHelper(t *testing.T) {
|
||||||
|
s1, s2 := StrStr(twoStrNoThrow())
|
||||||
|
if s1 != "test" || s2 != "test" {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_copyFile() {
|
||||||
|
copyFile := func(src, dst string) (err Err) {
|
||||||
|
defer Annotate(&err, fmt.Sprintf("copy %s %s", src, dst))
|
||||||
|
|
||||||
|
// These helpers are as fast as Check() calls
|
||||||
|
r := File(os.Open(src))
|
||||||
|
defer Check(r.Close())
|
||||||
|
|
||||||
|
w := File(os.Create(dst))
|
||||||
|
defer Handle(&err, func() {
|
||||||
|
os.Remove(dst)
|
||||||
|
})
|
||||||
|
defer Check(w.Close())
|
||||||
|
Empty(io.Copy(w, r))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := copyFile("/notfound/path/file.go", "/notfound/path/file.bak")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
//nolint:lll
|
||||||
|
// Output: copy /notfound/path/file.go /notfound/path/file.bak: open /notfound/path/file.go: no such file or directory
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkTry_StringHelper(b *testing.B) {
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
_ = String(noThrow())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkTry_XHelper(b *testing.B) {
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
_ = X(noThrow()).(string)
|
||||||
|
}
|
||||||
|
}
|
68
try/try.go
68
try/try.go
|
@ -46,8 +46,8 @@ type call struct {
|
||||||
Line int `json:"l"`
|
Line int `json:"l"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// fromErr wraps a standard error into a try-compatible one with extended stack info.
|
// FromErr wraps a standard error into a try-compatible one with extended stack info.
|
||||||
func fromErr(err error) Err {
|
func FromErr(err error) Err {
|
||||||
// If the error is already try-compatible, return
|
// If the error is already try-compatible, return
|
||||||
//nolint:errorlint
|
//nolint:errorlint
|
||||||
cterr, ok := err.(Err)
|
cterr, ok := err.(Err)
|
||||||
|
@ -88,9 +88,9 @@ func fromErr(err error) Err {
|
||||||
return terr
|
return terr
|
||||||
}
|
}
|
||||||
|
|
||||||
// newErr creates a new try-compatible error with extended stack info.
|
// NewErr creates a new try-compatible error with extended stack info.
|
||||||
func newErr(msg string) Err {
|
func NewErr(msg string) Err {
|
||||||
return fromErr(errors.New(msg))
|
return FromErr(errors.New(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error returns the standard go error message (initial message + annotations).
|
// Error returns the standard go error message (initial message + annotations).
|
||||||
|
@ -144,37 +144,41 @@ func (e *tryErr) Annotate(msg string) {
|
||||||
e.annotations = append(e.annotations, msg)
|
e.annotations = append(e.annotations, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// To is a helper function to call functions which returns (error)
|
// Empty is a helper method to handle errors of func() (string, error) functions.
|
||||||
// and check the error value. If error occurs it panics the error where err2
|
func Empty(_ interface{}, err error) {
|
||||||
// handlers can catch it if needed.
|
Check(err)
|
||||||
func To(err error) {
|
}
|
||||||
|
|
||||||
|
// Any is as similar as proposed Go2 Try macro, but it's a function and it
|
||||||
|
// returns slice of interfaces. It has quite big performance penalty when
|
||||||
|
// compared to Check function.
|
||||||
|
func Any(args ...interface{}) []interface{} {
|
||||||
|
check(args)
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check performs the error check for the given argument. If the err is nil,
|
||||||
|
// it does nothing. According the measurements, it's as fast as if err != nil
|
||||||
|
// {return err} on happy path.
|
||||||
|
func Check(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fromErr(err))
|
panic(FromErr(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// To1 is a helper function to call functions which returns (any, error)
|
// Checks the error status of the last argument. It panics with "wrong
|
||||||
// and check the error value. If error occurs it panics the error where err2
|
// signature" if the last calling parameter is not error. In case of error it
|
||||||
// handlers can catch it if needed.
|
// delivers it by panicking.
|
||||||
func To1[T any](v T, err error) T {
|
func check(args []interface{}) {
|
||||||
To(err)
|
argCount := len(args)
|
||||||
return v
|
last := argCount - 1
|
||||||
}
|
if args[last] != nil {
|
||||||
|
err, ok := args[last].(error)
|
||||||
// To2 is a helper function to call functions which returns (any, any, error)
|
if !ok {
|
||||||
// and check the error value. If error occurs it panics the error where err2
|
panic("wrong signature")
|
||||||
// handlers can catch it if needed.
|
}
|
||||||
func To2[T, U any](v1 T, v2 U, err error) (T, U) {
|
panic(FromErr(err))
|
||||||
To(err)
|
}
|
||||||
return v1, v2
|
|
||||||
}
|
|
||||||
|
|
||||||
// To3 is a helper function to call functions which returns (any, any, any, error)
|
|
||||||
// and check the error value. If error occurs it panics the error where err2
|
|
||||||
// handlers can catch it if needed.
|
|
||||||
func To3[T, U, V any](v1 T, v2 U, v3 V, err error) (T, U, V) {
|
|
||||||
To(err)
|
|
||||||
return v1, v2, v3
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is for adding an error handler to a function by defer. It's for
|
// Handle is for adding an error handler to a function by defer. It's for
|
||||||
|
|
|
@ -21,12 +21,14 @@ func twoStrNoThrow() (string, string, error) { return "test", "test", nil }
|
||||||
|
|
||||||
func noThrow() (string, error) { return "test", nil }
|
func noThrow() (string, error) { return "test", nil }
|
||||||
|
|
||||||
|
func wrongSignature() (int, int) { return 0, 0 }
|
||||||
|
|
||||||
func recursion(a int) int {
|
func recursion(a int) int {
|
||||||
if a == 0 {
|
if a == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
s, err := noThrow()
|
s, err := noThrow()
|
||||||
To(err)
|
Check(err)
|
||||||
_ = s
|
_ = s
|
||||||
return a + recursion(a-1)
|
return a + recursion(a-1)
|
||||||
}
|
}
|
||||||
|
@ -51,14 +53,14 @@ func noErr() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_noError(t *testing.T) {
|
func TestAny_noError(t *testing.T) {
|
||||||
x := To1(noThrow())
|
d := Any(noThrow())
|
||||||
if x != "test" {
|
if d[0].(string) != "test" {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
x, y := To2(twoStrNoThrow())
|
d = Any(twoStrNoThrow())
|
||||||
if x != "test" || y != "test" {
|
if d[0].(string) != "test" || d[1].(string) != "test" {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +69,7 @@ func TestDefault_Error(t *testing.T) {
|
||||||
var err Err
|
var err Err
|
||||||
defer Return(&err)
|
defer Return(&err)
|
||||||
|
|
||||||
To1(throw())
|
Any(throw())
|
||||||
|
|
||||||
t.Fail() // If everything works we are never here
|
t.Fail() // If everything works we are never here
|
||||||
}
|
}
|
||||||
|
@ -76,7 +78,7 @@ func TestAny_Error(t *testing.T) {
|
||||||
var err Err
|
var err Err
|
||||||
defer Handle(&err, func() {})
|
defer Handle(&err, func() {})
|
||||||
|
|
||||||
To1(throw())
|
Any(throw())
|
||||||
|
|
||||||
t.Fail() // If everything works we are never here
|
t.Fail() // If everything works we are never here
|
||||||
}
|
}
|
||||||
|
@ -85,13 +87,13 @@ func panickingHandle() {
|
||||||
var err Err
|
var err Err
|
||||||
defer Handle(&err, func() {})
|
defer Handle(&err, func() {})
|
||||||
|
|
||||||
panic("test")
|
Any(wrongSignature())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPanickingCarryOn_Handle(t *testing.T) {
|
func TestPanickingCarryOn_Handle(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if recover() == nil {
|
if recover() == nil {
|
||||||
t.Error("panics should go through when not our errors")
|
t.Error("panics should went thru when not our errors")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
panickingHandle()
|
panickingHandle()
|
||||||
|
@ -100,7 +102,7 @@ func TestPanickingCarryOn_Handle(t *testing.T) {
|
||||||
func panickingCatchAll() {
|
func panickingCatchAll() {
|
||||||
defer CatchAll(func(err Err) {}, func(v interface{}) {})
|
defer CatchAll(func(err Err) {}, func(v interface{}) {})
|
||||||
|
|
||||||
panickingHandle()
|
Any(wrongSignature())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPanickingCatchAll(t *testing.T) {
|
func TestPanickingCatchAll(t *testing.T) {
|
||||||
|
@ -116,7 +118,7 @@ func panickingReturn() {
|
||||||
var err Err
|
var err Err
|
||||||
defer Return(&err)
|
defer Return(&err)
|
||||||
|
|
||||||
panickingHandle()
|
Any(wrongSignature())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPanicking_Return(t *testing.T) {
|
func TestPanicking_Return(t *testing.T) {
|
||||||
|
@ -131,7 +133,7 @@ func TestPanicking_Return(t *testing.T) {
|
||||||
func panickingCatch() {
|
func panickingCatch() {
|
||||||
defer Catch(func(err Err) {})
|
defer Catch(func(err Err) {})
|
||||||
|
|
||||||
panickingHandle()
|
Any(wrongSignature())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPanicking_Catch(t *testing.T) {
|
func TestPanicking_Catch(t *testing.T) {
|
||||||
|
@ -148,13 +150,13 @@ func TestCatch_Error(t *testing.T) {
|
||||||
// fmt.Printf("error and defer handling:%s\n", err)
|
// fmt.Printf("error and defer handling:%s\n", err)
|
||||||
})
|
})
|
||||||
|
|
||||||
To1(throw())
|
Any(throw())
|
||||||
|
|
||||||
t.Fail() // If everything works we are never here
|
t.Fail() // If everything works we are never here
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewTryErr(t *testing.T) {
|
func TestNewTryErr(t *testing.T) {
|
||||||
tryErr := newErr("I f*cked up")
|
tryErr := NewErr("I f*cked up")
|
||||||
callStack := tryErr.CallStackString()
|
callStack := tryErr.CallStackString()
|
||||||
|
|
||||||
if !strings.HasPrefix(callStack, "ERROR: I f*cked up\n") {
|
if !strings.HasPrefix(callStack, "ERROR: I f*cked up\n") {
|
||||||
|
@ -170,7 +172,7 @@ func TestNewTryErr(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetData(t *testing.T) {
|
func TestGetData(t *testing.T) {
|
||||||
tryErr := newErr("I f*cked up")
|
tryErr := NewErr("I f*cked up")
|
||||||
tryErr.Annotate("test1")
|
tryErr.Annotate("test1")
|
||||||
tryErr.Annotate("test2")
|
tryErr.Annotate("test2")
|
||||||
data := tryErr.GetData()
|
data := tryErr.GetData()
|
||||||
|
@ -197,7 +199,7 @@ func TestGetData(t *testing.T) {
|
||||||
|
|
||||||
func TestErrCompare(t *testing.T) {
|
func TestErrCompare(t *testing.T) {
|
||||||
err := errors.New("TestError")
|
err := errors.New("TestError")
|
||||||
tryErr := fromErr(err)
|
tryErr := FromErr(err)
|
||||||
|
|
||||||
if !errors.Is(tryErr, err) {
|
if !errors.Is(tryErr, err) {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
|
@ -207,7 +209,7 @@ func TestErrCompare(t *testing.T) {
|
||||||
func TestReturnStd(t *testing.T) {
|
func TestReturnStd(t *testing.T) {
|
||||||
tf := func() (err error) {
|
tf := func() (err error) {
|
||||||
defer ReturnStd(&err)
|
defer ReturnStd(&err)
|
||||||
To1(throw())
|
String(throw())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,11 +221,11 @@ func TestReturnStd(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckTryErr(t *testing.T) {
|
func TestCheckTryErr(t *testing.T) {
|
||||||
testErr := newErr("TestErr")
|
testErr := NewErr("TestErr")
|
||||||
|
|
||||||
tf := func() (err Err) {
|
tf := func() (err Err) {
|
||||||
defer Return(&err)
|
defer Return(&err)
|
||||||
To(testErr)
|
Check(testErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,14 +239,14 @@ func TestCheckTryErr(t *testing.T) {
|
||||||
func ExampleReturn() {
|
func ExampleReturn() {
|
||||||
var err Err
|
var err Err
|
||||||
defer Return(&err)
|
defer Return(&err)
|
||||||
To1(noThrow())
|
Any(noThrow())
|
||||||
// Output:
|
// Output:
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleAnnotate() {
|
func ExampleAnnotate() {
|
||||||
annotated := func() (err Err) {
|
annotated := func() (err Err) {
|
||||||
defer Annotate(&err, "annotated")
|
defer Annotate(&err, "annotated")
|
||||||
To1(throw())
|
Any(throw())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err := annotated()
|
err := annotated()
|
||||||
|
@ -256,7 +258,7 @@ func ExampleAnnotate_deferStack() {
|
||||||
annotated := func() (err Err) {
|
annotated := func() (err Err) {
|
||||||
defer Annotate(&err, "annotated 2nd")
|
defer Annotate(&err, "annotated 2nd")
|
||||||
defer Annotate(&err, "annotated 1st")
|
defer Annotate(&err, "annotated 1st")
|
||||||
To1(throw())
|
Any(throw())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err := annotated()
|
err := annotated()
|
||||||
|
@ -269,7 +271,7 @@ func ExampleHandle() {
|
||||||
defer Handle(&err, func() {
|
defer Handle(&err, func() {
|
||||||
err.Annotate(fmt.Sprintf("error with (%d, %d)", a, b))
|
err.Annotate(fmt.Sprintf("error with (%d, %d)", a, b))
|
||||||
})
|
})
|
||||||
To1(throw())
|
Any(throw())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err := doSomething(1, 2)
|
err := doSomething(1, 2)
|
||||||
|
@ -288,34 +290,34 @@ func BenchmarkOldErrorCheckingWithIfClause(b *testing.B) {
|
||||||
|
|
||||||
func BenchmarkAny(b *testing.B) {
|
func BenchmarkAny(b *testing.B) {
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
To1(noThrow())
|
Any(noThrow())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkAny_ErrVar(b *testing.B) {
|
func BenchmarkAny_ErrVar(b *testing.B) {
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
_, err := noThrow()
|
_, err := noThrow()
|
||||||
To(err)
|
Any(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCheckInsideCall(b *testing.B) {
|
func BenchmarkCheckInsideCall(b *testing.B) {
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
To(noErr())
|
Check(noErr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCheckVarCall(b *testing.B) {
|
func BenchmarkCheckVarCall(b *testing.B) {
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
err := noErr()
|
err := noErr()
|
||||||
To(err)
|
Check(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCheck_ErrVar(b *testing.B) {
|
func BenchmarkCheck_ErrVar(b *testing.B) {
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
_, err := noThrow()
|
_, err := noThrow()
|
||||||
To(err)
|
Check(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
29
try/types.csv
Normal file
29
try/types.csv
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
Bool+s;bool
|
||||||
|
Byte+s;byte
|
||||||
|
Rune+s;rune
|
||||||
|
String+s;string
|
||||||
|
|
||||||
|
Int+s;int
|
||||||
|
Int8+s;int8
|
||||||
|
Int16+s;int16
|
||||||
|
Int32+s;int32
|
||||||
|
Int64+s;int64
|
||||||
|
|
||||||
|
Uint+s;uint
|
||||||
|
Uint8+s;uint8
|
||||||
|
Uint16+s;uint16
|
||||||
|
Uint32+s;uint32
|
||||||
|
Uint64+s;uint64
|
||||||
|
|
||||||
|
Float32+s;float32
|
||||||
|
Float64+s;float64
|
||||||
|
|
||||||
|
StrStr;string, string
|
||||||
|
X;interface{}
|
||||||
|
|
||||||
|
File;*os.File;os
|
||||||
|
Reader;io.Reader;io
|
||||||
|
Writer;io.Writer;io
|
||||||
|
Request;*http.Request;net/http
|
||||||
|
Response;*http.Response;net/http
|
||||||
|
Url;*url.URL;net/url
|
Can't render this file because it has a wrong number of fields in line 24.
|
31
try/types.tmpl
Normal file
31
try/types.tmpl
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package {{.PkgName}}
|
||||||
|
|
||||||
|
// gotry auto-generated type definitions. DO NOT EDIT.
|
||||||
|
|
||||||
|
import (
|
||||||
|
{{- if .UseExt }}
|
||||||
|
"code.thetadev.de/ThetaDev/gotry/try"
|
||||||
|
{{- end}}
|
||||||
|
{{- range $pkg, $b := .Imports}}
|
||||||
|
"{{$pkg}}"
|
||||||
|
{{- end}}
|
||||||
|
)
|
||||||
|
{{range $d := .Definitions }}
|
||||||
|
// {{$d.Name}} is a helper method to handle errors of
|
||||||
|
// func() ({{range $i, $t := $d.Types}}{{$t}}, {{end}}error) functions.
|
||||||
|
func {{$d.Name}}({{range $i, $t := $d.Types}}v{{if $i}}{{$i}}{{end}} {{$t}}, {{end}}err error) (
|
||||||
|
{{- range $i, $t := $d.Types}}{{if $i}}, {{end}}{{$t}}{{end}}) {
|
||||||
|
{{if .UseExt}}try.{{end}}Check(err)
|
||||||
|
return {{range $i, $t := $d.Types}}{{if $i}}, v{{$i}}{{else}}v{{end}}{{end}}
|
||||||
|
}
|
||||||
|
{{- if $d.WithSlice}}
|
||||||
|
|
||||||
|
// {{$d.Name}}s is a helper method to handle errors of
|
||||||
|
// func() ({{range $i, $t := $d.Types}}[]{{$t}}, {{end}}error) functions.
|
||||||
|
func {{$d.Name}}s({{range $i, $t := $d.Types}}v{{if $i}}{{$i}}{{end}} []{{$t}}, {{end}}err error) (
|
||||||
|
{{- range $i, $t := $d.Types}}{{if $i}}, {{end}}[]{{$t}}{{end}}) {
|
||||||
|
{{if .UseExt}}try.{{end}}Check(err)
|
||||||
|
return {{range $i, $t := $d.Types}}{{if $i}}, v{{$i}}{{else}}v{{end}}{{end}}
|
||||||
|
}
|
||||||
|
{{- end}}
|
||||||
|
{{end}}
|
Loading…
Reference in a new issue