New Upstream Release - golang-github-getlantern-errors

Ready changes

Summary

Merged new upstream version: 1.0.3 (was: 1.0.1).

Diff

diff --git a/LICENSE b/LICENSE
index 416dd69..2d1cb59 100644
--- a/LICENSE
+++ b/LICENSE
@@ -187,7 +187,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright 2018 Brave New Software Project, Inc.
+   Copyright 2021 Brave New Software Project, Inc.
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/debian/changelog b/debian/changelog
index 19d6924..101c0cf 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+golang-github-getlantern-errors (1.0.3-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 07 Jun 2023 21:37:40 -0000
+
 golang-github-getlantern-errors (0.0~git20190325.abdb3e3-2) unstable; urgency=medium
 
   * Add missing (build-)dep on getlantern-hex (Closes: #927250)
diff --git a/errors.go b/errors.go
index 8b2b84f..8ff64d6 100644
--- a/errors.go
+++ b/errors.go
@@ -8,8 +8,8 @@ Package errors defines error types used across Lantern project.
 
 or
 
-  n, err := Foo()
-	return n, errors.Wrap(err)
+	  n, err := Foo()
+		return n, errors.Wrap(err)
 
 New() method will create a new error with err as its cause. Wrap will wrap err,
 returning nil if err is nil.  If err is an error from Go's standard library,
@@ -18,15 +18,15 @@ return value of err.Error().
 
 One can record the operation on which the error occurred using Op():
 
-  return n, errors.New("Unable to do Foo: %v", err).Op("FooDooer")
+	return n, errors.New("Unable to do Foo: %v", err).Op("FooDooer")
 
 One can also record additional data:
 
-  return n, errors.
-		New("Unable to do Foo: %v", err).
-		Op("FooDooer").
-		With("mydata", "myvalue").
-		With("moredata", 5)
+	  return n, errors.
+			New("Unable to do Foo: %v", err).
+			Op("FooDooer").
+			With("mydata", "myvalue").
+			With("moredata", 5)
 
 When used with github.com/getlantern/ops, Error captures its current context
 and propagates that data for use in calling layers.
@@ -81,7 +81,6 @@ import (
 	"unicode"
 
 	"github.com/getlantern/context"
-	"github.com/getlantern/hidden"
 	"github.com/getlantern/ops"
 	"github.com/go-stack/stack"
 )
@@ -123,13 +122,9 @@ type Error interface {
 	RootCause() error
 }
 
-type structured struct {
-	id        uint64
-	hiddenID  string
+type baseError struct {
 	data      context.Map
 	context   context.Map
-	wrapped   error
-	cause     Error
 	callStack stack.CallStack
 }
 
@@ -142,16 +137,22 @@ func New(desc string, args ...interface{}) Error {
 // NewOffset is like New but offsets the stack by the given offset. This is
 // useful for utilities like golog that may create errors on behalf of others.
 func NewOffset(offset int, desc string, args ...interface{}) Error {
-	var cause error
+	e := buildError(desc, fmt.Sprintf(desc, args...))
+	e.attachStack(2 + offset)
 	for _, arg := range args {
-		err, isError := arg.(error)
+		wrapped, isError := arg.(error)
 		if isError {
-			cause = err
-			break
+			op, _, _, extraData := parseError(wrapped)
+			if op != "" {
+				e.Op(op)
+			}
+			for k, v := range extraData {
+				e.data[k] = v
+			}
+			we := &wrappingError{e, wrapped}
+			return we
 		}
 	}
-	e := buildError(desc, fmt.Sprintf(desc, args...), nil, Wrap(cause))
-	e.attachStack(2 + offset)
 	return e
 }
 
@@ -160,33 +161,57 @@ func NewOffset(offset int, desc string, args ...interface{}) Error {
 // errors.Wrap(s.l.Close()) regardless there's an error or not. If the error is
 // already wrapped, it is returned as is.
 func Wrap(err error) Error {
-	return wrapSkipFrames(err, 1)
+	if err == nil {
+		return nil
+	}
+	if e, ok := err.(Error); ok {
+		return e
+	}
+
+	op, goType, desc, extraData := parseError(err)
+	if desc == "" {
+		desc = err.Error()
+	}
+	e := buildError(desc, desc)
+	e.attachStack(2)
+	if op != "" {
+		e.Op(op)
+	}
+	e.data["error_type"] = goType
+	for k, v := range extraData {
+		e.data[k] = v
+	}
+
+	// Make sure we maintain existing wrapping.
+	if uw, ok := err.(unwrapper); ok {
+		return &wrappingError{e, uw.Unwrap()}
+	}
+
+	return e
 }
 
 // Fill implements the method from the context.Contextual interface.
-func (e *structured) Fill(m context.Map) {
-	if e != nil {
-		if e.cause != nil {
-			// Include data from cause, which supercedes context
-			e.cause.Fill(m)
-		}
-		// Include the context, which supercedes the cause
-		for key, value := range e.context {
-			m[key] = value
-		}
-		// Now include the error's data, which supercedes everything
-		for key, value := range e.data {
-			m[key] = value
-		}
+func (e *baseError) Fill(m context.Map) {
+	if e == nil {
+		return
+	}
+
+	// Include the context, which supercedes the cause
+	for key, value := range e.context {
+		m[key] = value
+	}
+	// Now include the error's data, which supercedes everything
+	for key, value := range e.data {
+		m[key] = value
 	}
 }
 
-func (e *structured) Op(op string) Error {
+func (e *baseError) Op(op string) Error {
 	e.data["error_op"] = op
 	return e
 }
 
-func (e *structured) With(key string, value interface{}) Error {
+func (e *baseError) With(key string, value interface{}) Error {
 	parts := strings.FieldsFunc(key, func(c rune) bool {
 		return !unicode.IsLetter(c) && !unicode.IsNumber(c)
 	})
@@ -204,140 +229,164 @@ func (e *structured) With(key string, value interface{}) Error {
 	return e
 }
 
-func (e *structured) RootCause() error {
-	if e.cause == nil {
-		if e.wrapped != nil {
-			return e.wrapped
-		}
-		return e
-	}
-	return e.cause.RootCause()
+func (e *baseError) RootCause() error {
+	return e
 }
 
-func (e *structured) ErrorClean() string {
+func (e *baseError) ErrorClean() string {
 	return e.data["error"].(string)
 }
 
 // Error satisfies the error interface
-func (e *structured) Error() string {
-	return e.data["error_text"].(string) + e.hiddenID
+func (e *baseError) Error() string {
+	return e.data["error_text"].(string)
+}
+
+func (e *baseError) MultiLinePrinter() func(*bytes.Buffer) bool {
+	return e.topLevelPrinter()
 }
 
-func (e *structured) MultiLinePrinter() func(buf *bytes.Buffer) bool {
-	first := true
-	indent := false
-	err := e
+func (e *baseError) topLevelPrinter() func(*bytes.Buffer) bool {
+	printingStack := false
 	stackPosition := 0
-	switchedCause := false
 	return func(buf *bytes.Buffer) bool {
-		if indent {
-			buf.WriteString("  ")
-		}
-		if first {
+		if !printingStack {
 			buf.WriteString(e.Error())
-			first = false
-			indent = true
-			return true
-		}
-		if switchedCause {
-			fmt.Fprintf(buf, "Caused by: %v", err)
-			if err.callStack != nil && len(err.callStack) > 0 {
-				switchedCause = false
-				indent = true
-				return true
-			}
-			if err.cause == nil {
-				return false
-			}
-			err = err.cause.(*structured)
-			return true
-		}
-		if stackPosition < len(err.callStack) {
-			buf.WriteString("at ")
-			call := err.callStack[stackPosition]
-			fmt.Fprintf(buf, "%+n (%s:%d)", call, call, call)
-			stackPosition++
-		}
-		if stackPosition >= len(err.callStack) {
-			switch cause := err.cause.(type) {
-			case *structured:
-				err = cause
-				indent = false
-				stackPosition = 0
-				switchedCause = true
-			default:
-				return false
-			}
+			printingStack = true
+			return len(e.callStack) > 0
 		}
-		return err != nil
+		call := e.callStack[stackPosition]
+		fmt.Fprintf(buf, "  at %+n (%s:%d)", call, call, call)
+		stackPosition++
+		return stackPosition < len(e.callStack)
 	}
 }
 
-func wrapSkipFrames(err error, skip int) Error {
-	if err == nil {
-		return nil
-	}
-
-	// Look for *structureds
-	if e, ok := err.(*structured); ok {
-		return e
-	}
-
-	var cause Error
-	// Look for hidden *structureds
-	hiddenIDs, err2 := hidden.Extract(err.Error())
-	if err2 == nil && len(hiddenIDs) > 0 {
-		// Take the first hidden ID as our cause
-		cause = get(hiddenIDs[0])
-	}
-
-	// Create a new *structured
-	return buildError("", "", err, cause)
-}
-
-func (e *structured) attachStack(skip int) {
+func (e *baseError) attachStack(skip int) {
 	call := stack.Caller(skip)
 	e.callStack = stack.Trace().TrimBelow(call)
 	e.data["error_location"] = fmt.Sprintf("%+n (%s:%d)", call, call, call)
 }
 
-func buildError(desc string, fullText string, wrapped error, cause Error) *structured {
-	e := &structured{
+func buildError(desc string, fullText string) *baseError {
+	e := &baseError{
 		data: make(context.Map),
 		// We capture the current context to allow it to propagate to higher layers.
 		context: ops.AsMap(nil, false),
-		wrapped: wrapped,
-		cause:   cause,
 	}
-	e.save()
 
-	errorType := "errors.Error"
-	if wrapped != nil {
-		op, goType, wrappedDesc, extra := parseError(wrapped)
-		if desc == "" {
-			desc = wrappedDesc
+	e.data["error"] = desc
+	if fullText != "" {
+		e.data["error_text"] = fullText
+	} else {
+		e.data["error_text"] = desc
+	}
+	e.data["error_type"] = "errors.Error"
+
+	return e
+}
+
+type topLevelPrinter interface {
+	// Returns a printer which prints only the top-level error and any associated stack trace. The
+	// output of this printer will be a prefix of the output from MultiLinePrinter().
+	topLevelPrinter() func(*bytes.Buffer) bool
+}
+
+type unwrapper interface {
+	Unwrap() error
+}
+
+type wrappingError struct {
+	*baseError
+	wrapped error
+}
+
+// Implements error unwrapping as described in the standard library's errors package:
+// https://golang.org/pkg/errors/#pkg-overview
+func (e *wrappingError) Unwrap() error {
+	return e.wrapped
+}
+
+func (e *wrappingError) Fill(m context.Map) {
+	type filler interface{ Fill(context.Map) }
+
+	applyToChain(e.wrapped, func(err error) {
+		if f, ok := err.(filler); ok {
+			f.Fill(m)
 		}
-		e.Op(op)
-		errorType = goType
-		if extra != nil {
-			for key, value := range extra {
-				e.data[key] = value
-			}
+	})
+	e.baseError.Fill(m)
+}
+
+func (e *wrappingError) RootCause() error {
+	return unwrapToRoot(e)
+}
+
+func (e *wrappingError) MultiLinePrinter() func(*bytes.Buffer) bool {
+	var (
+		currentPrinter = e.baseError.topLevelPrinter()
+		nextErr        = e.wrapped
+		prefix         = ""
+	)
+	return func(buf *bytes.Buffer) bool {
+		fmt.Fprint(buf, prefix)
+		if currentPrinter(buf) {
+			prefix = ""
+			return true
+		}
+		if nextErr == nil {
+			return false
+		}
+		currentPrinter = getTopLevelPrinter(nextErr)
+		prefix = "Caused by: "
+		if uw, ok := nextErr.(unwrapper); ok {
+			nextErr = uw.Unwrap()
+		} else {
+			nextErr = nil
 		}
+		return true
 	}
+}
 
-	cleanedDesc := hidden.Clean(desc)
-	e.data["error"] = cleanedDesc
-	if fullText != "" {
-		e.data["error_text"] = hidden.Clean(fullText)
-	} else {
-		e.data["error_text"] = cleanedDesc
+// We have to implement these two methods or the fluid syntax will result in the embedded *baseError
+// being returned, not the *wrappingError.
+
+func (e *wrappingError) Op(op string) Error {
+	e.baseError = e.baseError.Op(op).(*baseError)
+	return e
+}
+
+func (e *wrappingError) With(key string, value interface{}) Error {
+	e.baseError = e.baseError.With(key, value).(*baseError)
+	return e
+}
+
+func getTopLevelPrinter(err error) func(*bytes.Buffer) bool {
+	if tlp, ok := err.(topLevelPrinter); ok {
+		return tlp.topLevelPrinter()
+	}
+	return func(buf *bytes.Buffer) bool {
+		fmt.Fprint(buf, err)
+		return false
 	}
-	e.data["error_type"] = errorType
+}
 
+func unwrapToRoot(e error) error {
+	if uw, ok := e.(unwrapper); ok {
+		return unwrapToRoot(uw.Unwrap())
+	}
 	return e
 }
 
+// Applies f to the chain of errors unwrapped from err. The function is applied to the root cause
+// first and err last.
+func applyToChain(err error, f func(error)) {
+	if uw, ok := err.(unwrapper); ok {
+		applyToChain(uw.Unwrap(), f)
+	}
+	f(err)
+}
+
 func parseError(err error) (op string, goType string, desc string, extra map[string]string) {
 	extra = make(map[string]string)
 
diff --git a/errors_test.go b/errors_test.go
index 7c4887a..d0f78d5 100644
--- a/errors_test.go
+++ b/errors_test.go
@@ -2,63 +2,33 @@ package errors
 
 import (
 	"bytes"
+	"errors"
 	"fmt"
+	"io"
+	"net"
 	"regexp"
 	"testing"
 
 	"github.com/getlantern/context"
-	"github.com/getlantern/hidden"
-	"github.com/getlantern/ops"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 var (
-	replaceNumbers = regexp.MustCompile("[0-9]+")
+	replaceNumbers = regexp.MustCompile(`[0-9]+`)
+	replaceArch    = regexp.MustCompile(`asm_[a-z0-9]+\.s`)
 )
 
-func TestFull(t *testing.T) {
-	var firstErr Error
-
-	// Iterate past the size of the hidden buffer
-	for i := 0; i < len(hiddenErrors)*2; i++ {
-		op := ops.Begin("op1").Set("ca", 100).Set("cd", 100)
-		e := New("Hello %v", "There").Op("My Op").With("DaTa_1", 1)
-		op.End()
-		if firstErr == nil {
-			firstErr = e
-		}
-		assert.Equal(t, "Hello There", e.Error()[:11])
-		op = ops.Begin("op2").Set("ca", 200).Set("cb", 200).Set("cc", 200)
-		e3 := Wrap(fmt.Errorf("I'm wrapping your text: %v", e)).Op("outer op").With("dATA+1", i).With("cb", 300)
-		op.End()
-		assert.Equal(t, e, e3.(*structured).cause, "Wrapping a regular error should have extracted the contained *Error")
-		m := make(context.Map)
-		e3.Fill(m)
-		assert.Equal(t, i, m["data_1"], "Error's data should dominate all")
-		assert.Equal(t, 200, m["ca"], "Error's context should dominate cause")
-		assert.Equal(t, 300, m["cb"], "Error's data should dominate its context")
-		assert.Equal(t, 200, m["cc"], "Error's context should come through")
-		assert.Equal(t, 100, m["cd"], "Cause's context should come through")
-		assert.Equal(t, "My Op", e.(*structured).data["error_op"], "Op should be available from cause")
-
-		for _, call := range e3.(*structured).callStack {
-			t.Logf("at %v", call)
-		}
-	}
-
-	e3 := Wrap(fmt.Errorf("I'm wrapping your text: %v", firstErr)).With("a", 2)
-	assert.Nil(t, e3.(*structured).cause, "Wrapping an *Error that's no longer buffered should have yielded no cause")
-}
-
 func TestNewWithCause(t *testing.T) {
 	cause := buildCause()
 	outer := New("Hello %v", cause)
-	assert.Equal(t, "Hello World", hidden.Clean(outer.Error()))
-	assert.Equal(t, "Hello %v", outer.(*structured).ErrorClean())
+	assert.Equal(t, "Hello World", outer.Error())
+	assert.Equal(t, "Hello %v", outer.ErrorClean())
+	require.IsType(t, (*wrappingError)(nil), outer, "Including an error arg should have resulted in a *wrappingError")
 	assert.Equal(t,
 		"github.com/getlantern/errors.TestNewWithCause (errors_test.go:999)",
-		replaceNumbers.ReplaceAllString(outer.(*structured).data["error_location"].(string), "999"))
-	assert.Equal(t, cause, outer.(*structured).cause)
+		replaceNumbers.ReplaceAllString(outer.(*wrappingError).data["error_location"].(string), "999"))
+	assert.Equal(t, cause, outer.(*wrappingError).wrapped)
 
 	// Make sure that stacktrace prints out okay
 	buf := &bytes.Buffer{}
@@ -73,12 +43,12 @@ func TestNewWithCause(t *testing.T) {
 	expected := `Hello World
   at github.com/getlantern/errors.TestNewWithCause (errors_test.go:999)
   at testing.tRunner (testing.go:999)
-  at runtime.goexit (asm_amd999.s:999)
+  at runtime.goexit (asm_arch.s:999)
 Caused by: World
   at github.com/getlantern/errors.buildCause (errors_test.go:999)
   at github.com/getlantern/errors.TestNewWithCause (errors_test.go:999)
   at testing.tRunner (testing.go:999)
-  at runtime.goexit (asm_amd999.s:999)
+  at runtime.goexit (asm_arch.s:999)
 Caused by: orld
 Caused by: ld
   at github.com/getlantern/errors.buildSubSubCause (errors_test.go:999)
@@ -86,11 +56,16 @@ Caused by: ld
   at github.com/getlantern/errors.buildCause (errors_test.go:999)
   at github.com/getlantern/errors.TestNewWithCause (errors_test.go:999)
   at testing.tRunner (testing.go:999)
-  at runtime.goexit (asm_amd999.s:999)
+  at runtime.goexit (asm_arch.s:999)
 Caused by: d
 `
 
-	assert.Equal(t, expected, replaceNumbers.ReplaceAllString(hidden.Clean(buf.String()), "999"))
+	assert.Equal(t,
+		expected,
+		replaceArch.ReplaceAllString(
+			replaceNumbers.ReplaceAllString(buf.String(), "999"),
+			"asm_arch.s",
+		))
 	assert.Equal(t, buildSubSubSubCause(), outer.RootCause())
 }
 
@@ -99,7 +74,7 @@ func buildCause() Error {
 }
 
 func buildSubCause() error {
-	return fmt.Errorf("or%v", buildSubSubCause())
+	return fmt.Errorf("or%w", buildSubSubCause())
 }
 
 func buildSubSubCause() error {
@@ -118,25 +93,50 @@ func doWrapNil() error {
 	return Wrap(nil)
 }
 
-func TestHiddenWithCause(t *testing.T) {
-	e1 := fmt.Errorf("I failed %v", "dude")
-	e2 := New("I wrap: %v", e1)
-	e3 := fmt.Errorf("Hiding %v", e2)
-	// clear hidden buffer
-	hiddenErrors = make([]*structured, 100)
-	e4 := Wrap(e3)
-	e5 := New("I'm really outer: %v", e4)
+func TestFill(t *testing.T) {
+	e := New("something happened").(*baseError)
+	e2 := New("uh oh: %v", e).(*wrappingError)
+	e3 := fmt.Errorf("hmm: %w", e2)
+	e4 := New("umm: %v", e3).(*wrappingError)
+
+	e4.data["name"] = "e4"
+	e2.data["name"] = "e2"
+	e.data["name"] = "e"
+	e2.data["k"] = "v2"
+	e.data["k"] = "v"
+	e.data["a"] = "b"
+
+	m := context.Map{}
+	e4.Fill(m)
+	require.Equal(t, "e4", m["name"])
+	require.Equal(t, "v2", m["k"])
+	require.Equal(t, "b", m["a"])
+}
 
-	buf := &bytes.Buffer{}
-	print := e5.MultiLinePrinter()
-	for {
-		more := print(buf)
-		buf.WriteByte('\n')
-		if !more {
-			break
-		}
-	}
-	fmt.Println(buf.String())
-	// We're not asserting the output because we're just making sure that printing
-	// doesn't panic. If we get to this point without panicking, we're happy.
+// Ensures that this package implements error unwrapping as described in:
+// https://golang.org/pkg/errors/#pkg-overview
+func TestUnwrapping(t *testing.T) {
+	sampleUnwrapper := fmt.Errorf("%w", fmt.Errorf("something happened"))
+
+	errNoCause := New("something happened")
+	_, ok := errNoCause.(unwrapper)
+	assert.False(t, ok, "error with no cause should not implement Unwrap method")
+	wrappedNoCause := Wrap(errNoCause)
+	_, ok = wrappedNoCause.(unwrapper)
+	assert.False(t, ok, "wrapped error with no cause should not implement Unwrap method")
+
+	errFromEOF := New("something happened: %v", io.EOF)
+	assert.Implements(t, &sampleUnwrapper, errFromEOF)
+	assert.True(t, errors.Is(errFromEOF, io.EOF))
+	wrappedFromEOF := Wrap(errFromEOF)
+	assert.Implements(t, &sampleUnwrapper, wrappedFromEOF)
+	assert.True(t, errors.Is(wrappedFromEOF, io.EOF))
+
+	addrErrHolder := new(net.AddrError)
+	errFromAddrErr := New("something happend: %v", new(net.AddrError))
+	assert.Implements(t, &sampleUnwrapper, errFromAddrErr)
+	assert.True(t, errors.As(errFromAddrErr, &addrErrHolder))
+	wrappedFromAddrErr := Wrap(errFromAddrErr)
+	assert.Implements(t, &sampleUnwrapper, wrappedFromAddrErr)
+	assert.True(t, errors.As(wrappedFromAddrErr, &addrErrHolder))
 }
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..f2580a5
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,12 @@
+module github.com/getlantern/errors
+
+go 1.0
+
+require (
+	github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201
+	github.com/getlantern/ops v0.0.0-20220713155959-1315d978fff7
+	github.com/go-stack/stack v1.8.1
+	github.com/stretchr/testify v1.8.0
+)
+
+require go.opentelemetry.io/otel v1.9.0 // indirect
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..74f9ddb
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,32 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201 h1:oEZYEpZo28Wdx+5FZo4aU7JFXu0WG/4wJWese5reQSA=
+github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201/go.mod h1:Y9WZUHEb+mpra02CbQ/QczLUe6f0Dezxaw5DCJlJQGo=
+github.com/getlantern/ops v0.0.0-20220713155959-1315d978fff7 h1:Od0xvR4iK3gZwhkIbxnHw4Teusv+n5G/F9dW7x+C2f0=
+github.com/getlantern/ops v0.0.0-20220713155959-1315d978fff7/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
+github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
+github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+go.opentelemetry.io/otel v1.9.0 h1:8WZNQFIB2a71LnANS9JeyidJKKGOOremcUtb/OtHISw=
+go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo=
+go.opentelemetry.io/otel/trace v1.9.0 h1:oZaCNJUjWcg60VXWee8lJKlqhPbXAPB51URuR47pQYc=
+go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/hide.go b/hide.go
deleted file mode 100644
index f10d863..0000000
--- a/hide.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package errors
-
-import (
-	"encoding/binary"
-	"sync"
-
-	"github.com/getlantern/hidden"
-)
-
-var (
-	hiddenErrors = make([]*structured, 100)
-	nextID       = uint64(0)
-	hiddenMutex  sync.RWMutex
-)
-
-// This trick saves the error to a ring buffer and embeds a non-printing
-// hiddenID in the error's description, so that if the errors is later wrapped
-// by a standard error using something like
-// fmt.Errorf("An error occurred: %v", thisError), we can subsequently extract
-// the error simply using the hiddenID in the string.
-func (e *structured) save() {
-	hiddenMutex.Lock()
-	b := make([]byte, 8)
-	binary.BigEndian.PutUint64(b, nextID)
-	e.id = nextID
-	e.hiddenID = hidden.ToString(b)
-	hiddenErrors[idxForID(nextID)] = e
-	nextID++
-	hiddenMutex.Unlock()
-}
-
-func get(hiddenID []byte) Error {
-	if len(hiddenID) != 8 {
-		return nil
-	}
-	id := binary.BigEndian.Uint64(hiddenID)
-	hiddenMutex.RLock()
-	err := hiddenErrors[idxForID(id)]
-	hiddenMutex.RUnlock()
-	if err != nil && err.id == id {
-		// Found it!
-		return err
-	}
-	// buffer has rolled over
-	return nil
-}
-
-func idxForID(id uint64) int {
-	return int(id % uint64(len(hiddenErrors)))
-}

More details

Full run details

Historical runs