commit 4e5775fcfabc19fe37a3d774a2c529b6ae544c5c
Author: Timon Ringwald <timon.ringwald@m2xpert.com>
Date:   Wed Sep 9 11:48:46 2020 +0200

    initial commit

diff --git a/calltrace.go b/calltrace.go
new file mode 100644
index 0000000..4a99722
--- /dev/null
+++ b/calltrace.go
@@ -0,0 +1,46 @@
+package adverr
+
+import (
+	"runtime"
+	"strconv"
+	"strings"
+)
+
+// CallTrace represents a call stack trace similar to Java's stack trace
+type CallTrace struct {
+	frames *runtime.Frames
+}
+
+// Trace returns a new CallTrace starting from this call
+// Use skip to skip the first entries in the trace
+func Trace(skip int) *CallTrace {
+	if !TraceCallStack {
+		return nil
+	}
+
+	pc := make([]uintptr, CallStackLength)
+	n := runtime.Callers(skip+1, pc)
+	pc = pc[:n]
+	return &CallTrace{runtime.CallersFrames(pc)}
+}
+
+func (ct *CallTrace) String() string {
+	if ct == nil {
+		return ""
+	}
+
+	b := new(strings.Builder)
+
+	for frame, ok := ct.frames.Next(); ok; frame, ok = ct.frames.Next() {
+		b.WriteString("\tat ")
+		b.WriteString(frame.Function)
+		b.WriteString(" (")
+		b.WriteString(frame.File)
+		b.WriteString(":")
+		b.WriteString(strconv.Itoa(frame.Line))
+		b.WriteString(")")
+		b.WriteString("\n")
+	}
+
+	return b.String()
+}
diff --git a/doc.go b/doc.go
new file mode 100644
index 0000000..bf988d4
--- /dev/null
+++ b/doc.go
@@ -0,0 +1,34 @@
+/*
+	Package adverr implements errors with call stack traces
+	as well as error templates for error equality
+
+	Usage examples
+
+	Creating templates:
+		var (
+			ErrDoStuffFailed = adverr.NewErrTmpl("ErrDoStuffFailed", "Could'nt do stuff because of %s")
+		)
+
+	Creating independent error (without error template):
+		func doStuffWithIndependentErr() error {
+			return adverr.New("Could'nt do stuff")
+		}
+
+	Creating error based on template:
+		func doStuff() error {
+			return ErrDoStuffFailed.New("reasons")
+		}
+
+	Printing errors on stderr convieniently:
+		Print(myErr)
+		Println(myErr)
+
+	Printing errors on stderr and exit with exitcode:
+		Fatal(myErr, 1)
+		Fatalln(myErr, 1)
+
+	Advantages of error templates
+		two errors made by the same template will return true when called with errors.Is()
+
+*/
+package adverr
diff --git a/error.go b/error.go
new file mode 100644
index 0000000..bc99bae
--- /dev/null
+++ b/error.go
@@ -0,0 +1,104 @@
+package adverr
+
+import (
+	"errors"
+	"reflect"
+	"strings"
+)
+
+// Error is a wrapper for error with stack trace
+type Error struct {
+	msg       string
+	callTrace *CallTrace
+	tmpl      *ErrTmpl
+	cause     error
+}
+
+// New returns a new Error with the given message
+func New(msg string) *Error {
+	return &Error{
+		msg:       msg,
+		cause:     nil,
+		tmpl:      nil,
+		callTrace: Trace(2),
+	}
+}
+
+// Wrap returns a new Error with the given message which is caused by cause
+func Wrap(msg string, cause error) *Error {
+	return &Error{
+		msg:       msg,
+		cause:     cause,
+		callTrace: Trace(2),
+	}
+}
+
+func errtype(err error) string {
+	if e, ok := err.(*Error); ok && e.tmpl != nil {
+		return errtype(e.tmpl)
+	} else if tmpl, ok := err.(*ErrTmpl); ok {
+		t := reflect.TypeOf(tmpl)
+		for t.Kind() == reflect.Ptr {
+			t = t.Elem()
+		}
+		return t.PkgPath() + "." + tmpl.name
+	}
+
+	t := reflect.TypeOf(err)
+	for t.Kind() == reflect.Ptr {
+		t = t.Elem()
+	}
+	return t.PkgPath() + "." + t.Name()
+}
+
+func (e *Error) Unwrap() error {
+	return e.cause
+}
+
+func (e *Error) Error() string {
+	b := new(strings.Builder)
+	printErr(e, b)
+	return b.String()
+}
+
+// Is implements the error equality function used by errors.Is()
+// It returns true if the error is the same instance or is created using the same ErrTmpl
+func (e *Error) Is(target error) bool {
+	// same error instance
+	if target == e {
+		return true
+	}
+
+	// no template used, therefore no equality possible
+	if e.tmpl == nil {
+		return false
+	}
+
+	// same template, therefore errors are equal to another
+	if tErr, ok := target.(*Error); ok {
+		return tErr.tmpl == e.tmpl
+	}
+
+	return false
+}
+
+func printErr(err error, b *strings.Builder) {
+	if e, ok := err.(*Error); ok {
+		b.WriteString(errtype(e))
+		b.WriteString(": ")
+		b.WriteString(e.msg)
+		b.WriteString("\n")
+		b.WriteString(e.callTrace.String())
+	} else {
+		b.WriteString(errtype(err))
+		b.WriteString(": ")
+		b.WriteString(err.Error())
+		b.WriteString("\n")
+	}
+
+	cause := errors.Unwrap(err)
+	if cause != nil {
+		b.WriteString("Caused by ")
+		printErr(cause, b)
+	}
+}
diff --git a/error_test.go b/error_test.go
new file mode 100644
index 0000000..2c9b384
--- /dev/null
+++ b/error_test.go
@@ -0,0 +1,17 @@
+package adverr
+
+import (
+	"errors"
+	"fmt"
+	"testing"
+)
+
+func TestErr(t *testing.T) {
+	TraceCallStack = false
+	err := doStuff()
+	fmt.Println(err)
+}
+
+func doStuff() error {
+	return Wrap("wrapped error", Wrap("test error", fmt.Errorf("asd: %w", errors.New("test"))))
+}
diff --git a/error_tmpl.go b/error_tmpl.go
new file mode 100644
index 0000000..11e088c
--- /dev/null
+++ b/error_tmpl.go
@@ -0,0 +1,35 @@
+package adverr
+
+import "fmt"
+
+var (
+	// ErrTmplUsedAsErr is returned from ErrTmpl.Error() because an error template should never be used as an actual error value
+	ErrTmplUsedAsErr = NewErrTmpl("ErrTmplUsedAsErr", "Error template used as error value: %s")
+)
+
+// ErrTmpl is an error template to define equalities between different errors
+type ErrTmpl struct {
+	name   string
+	format string
+}
+
+// NewErrTmpl returns a new error template with the given format string as a predefined error message
+func NewErrTmpl(name, format string) *ErrTmpl {
+	return &ErrTmpl{name, format}
+}
+
+// Error implementation just for satisfying the error interface
+// Please dont use ErrTmpls as actual errors
+func (t *ErrTmpl) Error() string {
+	return ErrTmplUsedAsErr.New(errtype(t)).Error()
+}
+
+// New returns a new Error in which the given values are being formatted into the format string of its template
+func (t *ErrTmpl) New(args ...interface{}) *Error {
+	return &Error{
+		msg:       fmt.Sprintf(t.format, args...),
+		cause:     nil,
+		tmpl:      t,
+		callTrace: Trace(2),
+	}
+}
diff --git a/error_tmpl_test.go b/error_tmpl_test.go
new file mode 100644
index 0000000..32629f5
--- /dev/null
+++ b/error_tmpl_test.go
@@ -0,0 +1,19 @@
+package adverr
+
+import (
+	"fmt"
+	"testing"
+)
+
+var (
+	ErrDoStuffFailed = NewErrTmpl("ErrDoStuffFailed", "test error: %s")
+)
+
+func TestErrTmpl(t *testing.T) {
+	err := doTemplateStuff()
+	fmt.Println(err)
+}
+
+func doTemplateStuff() error {
+	return ErrDoStuffFailed.New("because of reasons")
+}
diff --git a/globals.go b/globals.go
new file mode 100644
index 0000000..4c6de23
--- /dev/null
+++ b/globals.go
@@ -0,0 +1,9 @@
+package adverr
+
+// TraceCallStack decides if any call stack traces will be gathered when creating Errors
+// If your application is doing performance-heavy tasks with lots of Error creations, you may consider setting this to false
+// If set to false, all CallTraces in Error will be nil
+var TraceCallStack bool = true
+
+// CallStackLength decides how many calls from the call stack should be gathered at most
+var CallStackLength int = 100
diff --git a/utils.go b/utils.go
new file mode 100644
index 0000000..dba9e69
--- /dev/null
+++ b/utils.go
@@ -0,0 +1,28 @@
+package adverr
+
+import (
+	"fmt"
+	"os"
+)
+
+// Println prints the given err to stderr followed by a newline
+func Println(err error) {
+	fmt.Fprintln(os.Stderr, err)
+}
+
+// Print prints the given err to stderr
+func Print(err error) {
+	fmt.Fprint(os.Stderr, err)
+}
+
+// Fatalln prints the given err to stderr followed by a newline and exits immediately with the given exit code
+func Fatalln(err error, exitcode int) {
+	fmt.Fprintln(os.Stderr, err)
+	os.Exit(exitcode)
+}
+
+// Fatal prints the given err to stderr and exits immediately with the given exit code
+func Fatal(err error, exitcode int) {
+	fmt.Fprint(os.Stderr, err)
+	os.Exit(exitcode)
+}