|
| 1 | +// Copyright 2025 the libevm authors. |
| 2 | +// |
| 3 | +// The libevm additions to go-ethereum are free software: you can redistribute |
| 4 | +// them and/or modify them under the terms of the GNU Lesser General Public License |
| 5 | +// as published by the Free Software Foundation, either version 3 of the License, |
| 6 | +// or (at your option) any later version. |
| 7 | +// |
| 8 | +// The libevm additions are distributed in the hope that they will be useful, |
| 9 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser |
| 11 | +// General Public License for more details. |
| 12 | +// |
| 13 | +// You should have received a copy of the GNU Lesser General Public License |
| 14 | +// along with the go-ethereum library. If not, see |
| 15 | +// <http://www.gnu.org/licenses/>. |
| 16 | +// |
| 17 | +// Package errs provides a mechanism for [testing error semantics] through |
| 18 | +// unique identifiers, instead of depending on error messages that may result in |
| 19 | +// change-detector tests. |
| 20 | +// |
| 21 | +// [testing error semantics]: https://google.github.io/styleguide/go/decisions#test-error-semantics |
| 22 | +package errs |
| 23 | + |
| 24 | +import ( |
| 25 | + "errors" |
| 26 | + "fmt" |
| 27 | +) |
| 28 | + |
| 29 | +// An ID is a distinct numeric identifier for an error. It has no effect on the |
| 30 | +// error message and can only be accessed with this package. |
| 31 | +type ID int |
| 32 | + |
| 33 | +// Error returns a new error with the ID. |
| 34 | +func (id ID) Error(msg string) error { |
| 35 | + return noWrap{errors.New(msg), id} |
| 36 | +} |
| 37 | + |
| 38 | +type noWrap struct { |
| 39 | + error |
| 40 | + id ID |
| 41 | +} |
| 42 | + |
| 43 | +// Errorf is the formatted equivalent of [ID.Error], supporting the same |
| 44 | +// wrapping semantics as [fmt.Errorf]. |
| 45 | +func (id ID) Errorf(format string, a ...any) error { |
| 46 | + switch err := fmt.Errorf(format, a...).(type) { |
| 47 | + case singleWrapper: |
| 48 | + return single{err, id} |
| 49 | + case multiWrapper: |
| 50 | + return multi{err, id} |
| 51 | + default: |
| 52 | + return noWrap{err, id} |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +type singleWrapper interface { |
| 57 | + error |
| 58 | + Unwrap() error |
| 59 | +} |
| 60 | + |
| 61 | +type single struct { |
| 62 | + singleWrapper |
| 63 | + id ID |
| 64 | +} |
| 65 | + |
| 66 | +type multiWrapper interface { |
| 67 | + error |
| 68 | + Unwrap() []error |
| 69 | +} |
| 70 | + |
| 71 | +type multi struct { |
| 72 | + multiWrapper |
| 73 | + id ID |
| 74 | +} |
| 75 | + |
| 76 | +// IDOf returns the ID of the error, if one exists, and a flag to indicate as |
| 77 | +// such. IDOf does not unwrap errors. |
| 78 | +func IDOf(err error) (ID, bool) { |
| 79 | + switch err := err.(type) { |
| 80 | + case noWrap: |
| 81 | + return err.id, true |
| 82 | + case single: |
| 83 | + return err.id, true |
| 84 | + case multi: |
| 85 | + return err.id, true |
| 86 | + default: |
| 87 | + return 0, false |
| 88 | + } |
| 89 | +} |
0 commit comments