Skip to content

Commit 78ba2be

Browse files
committed
Init package
0 parents  commit 78ba2be

File tree

11 files changed

+477
-0
lines changed

11 files changed

+477
-0
lines changed

.github/workflows/main.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Test
2+
3+
on:
4+
- push
5+
- pull_request
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
name: Go ${{ matrix.go }}
11+
strategy:
12+
matrix:
13+
go:
14+
- '1.24'
15+
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
20+
- name: Setup Go ${{ matrix.go }}
21+
uses: actions/setup-go@v4
22+
with:
23+
go-version: ${{ matrix.go }}
24+
25+
- name: Install tools
26+
run: |
27+
go install honnef.co/go/tools/cmd/staticcheck@latest
28+
go install golang.org/x/vuln/cmd/govulncheck@latest
29+
30+
- name: Vet
31+
run: go vet ./...
32+
33+
- name: Lint
34+
run: staticcheck ./...
35+
36+
- name: Security
37+
run: govulncheck ./...
38+
39+
- name: Build
40+
run: go build -v ./...
41+
42+
- name: Test
43+
run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./...

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7+
8+
9+
## [Unreleased](https://github.com/gravitton/errors/compare/v0.1.0...master)
10+
11+
12+
## v0.1.0 (2025-08-03)
13+
### Added

LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# The MIT License (MIT)
2+
3+
Copyright (c) 2018 Tomáš Novotný <tomas.novotny.301@gmail.com>
4+
5+
> Permission is hereby granted, free of charge, to any person obtaining a copy
6+
> of this software and associated documentation files (the "Software"), to deal
7+
> in the Software without restriction, including without limitation the rights
8+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
> copies of the Software, and to permit persons to whom the Software is
10+
> furnished to do so, subject to the following conditions:
11+
>
12+
> The above copyright notice and this permission notice shall be included in
13+
> all copies or substantial portions of the Software.
14+
>
15+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
> THE SOFTWARE.

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Errors
2+
3+
[![Latest Stable Version][ico-release]][link-release]
4+
[![Build Status][ico-workflow]][link-workflow]
5+
[![Go Dev Reference][ico-go-dev-reference]][link-go-dev-reference]
6+
[![Software License][ico-license]][link-licence]
7+
8+
9+
## Installation
10+
11+
```bash
12+
go get github.com/gravitton/errors
13+
```
14+
15+
16+
## Usage
17+
18+
```go
19+
```
20+
21+
22+
## Credits
23+
24+
- [Tomáš Novotný](https://github.com/tomas-novotny)
25+
- [All Contributors][link-contributors]
26+
27+
28+
## License
29+
30+
The MIT License (MIT). Please see [License File][link-licence] for more information.
31+
32+
33+
[ico-license]: https://img.shields.io/github/license/gravitton/errors.svg?style=flat-square&colorB=blue
34+
[ico-workflow]: https://img.shields.io/github/actions/workflow/status/gravitton/errors/main.yml?branch=main&style=flat-square
35+
[ico-release]: https://img.shields.io/github/v/release/gravitton/errors?style=flat-square&colorB=blue
36+
[ico-go-dev-reference]: https://img.shields.io/badge/go.dev-reference-blue?style=flat-square
37+
38+
[link-author]: https://github.com/gravitton
39+
[link-release]: https://github.com/gravitton/errors/releases
40+
[link-contributors]: https://github.com/gravitton/errors/contributors
41+
[link-licence]: ./LICENSE.md
42+
[link-changelog]: ./CHANGELOG.md
43+
[link-workflow]: https://github.com/gravitton/errors/actions
44+
[link-go-dev-reference]: https://pkg.go.dev/github.com/gravitton/errors

doc.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package errors

error.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package errors
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"reflect"
7+
)
8+
9+
type DataError struct {
10+
err error
11+
data map[string]any
12+
cause error
13+
}
14+
15+
func New(text string) *DataError {
16+
return &DataError{
17+
err: errors.New(text),
18+
}
19+
}
20+
21+
func Newf(format string, v ...any) *DataError {
22+
return &DataError{
23+
err: fmt.Errorf(format, v...),
24+
}
25+
}
26+
27+
func Wrap(err any) *DataError {
28+
if err == nil {
29+
return nil
30+
}
31+
32+
var e error
33+
switch t := err.(type) {
34+
case *DataError:
35+
return t
36+
case error:
37+
e = t
38+
default:
39+
e = fmt.Errorf("%v", err)
40+
}
41+
42+
return &DataError{
43+
err: e,
44+
}
45+
}
46+
47+
func (e *DataError) Error() string {
48+
return e.err.Error()
49+
}
50+
51+
func (e *DataError) Fields() map[string]any {
52+
return e.data
53+
}
54+
55+
func (e *DataError) WithField(key string, value any) *DataError {
56+
return e.WithFields(map[string]any{key: value})
57+
}
58+
59+
func (e *DataError) WithFields(values map[string]any) *DataError {
60+
data := make(map[string]any, len(e.data)+len(values))
61+
for k, v := range e.data {
62+
data[k] = v
63+
}
64+
65+
for k, v := range values {
66+
if t := reflect.TypeOf(v); t != nil {
67+
switch {
68+
case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
69+
continue
70+
}
71+
}
72+
73+
data[k] = v
74+
}
75+
76+
return &DataError{
77+
err: e.err,
78+
data: data,
79+
cause: e.cause,
80+
}
81+
}
82+
83+
func (e *DataError) WithCause(err error) *DataError {
84+
return &DataError{
85+
err: e.err,
86+
data: e.data,
87+
cause: err,
88+
}
89+
}
90+
91+
func (e *DataError) Unwrap() error {
92+
if e.cause != nil {
93+
return e.cause
94+
}
95+
96+
return e.err
97+
}
98+
99+
func (e *DataError) Is(target error) bool {
100+
err, ok := target.(*DataError)
101+
if !ok {
102+
return false
103+
}
104+
105+
if e.Error() != target.Error() {
106+
return false
107+
}
108+
109+
for k, v := range err.Fields() {
110+
if e.Fields()[k] != v {
111+
return false
112+
}
113+
}
114+
115+
return true
116+
}

error_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package errors
2+
3+
import (
4+
"errors"
5+
"github.com/gravitton/assert"
6+
"reflect"
7+
"testing"
8+
)
9+
10+
func TestFields(t *testing.T) {
11+
err := Newf("Test DataError #%d: %s", 5, "failed to spawn")
12+
assert.Equal(t, err.Error(), "Test DataError #5: failed to spawn")
13+
assert.Length(t, err.Fields(), 0)
14+
15+
err = Wrap("test")
16+
assert.Equal(t, err.Error(), "test")
17+
18+
err = New("test3").WithField("action", "call")
19+
assert.Equal(t, err.Error(), "test3")
20+
assert.Equal(t, err.Fields()["action"], "call")
21+
assert.NotContains(t, err.Fields(), "type")
22+
23+
err1 := Wrap(err)
24+
assert.Same(t, err, err1)
25+
26+
err2 := err.WithFields(map[string]any{"type": "warning"})
27+
assert.NotSame(t, err1, err2)
28+
assert.NotContains(t, err.Fields(), "type")
29+
assert.Equal(t, err2.Fields()["type"], "warning")
30+
31+
err3 := err.WithField("action", "send")
32+
assert.Equal(t, err2.Fields()["action"], "call")
33+
assert.Equal(t, err3.Fields()["action"], "send")
34+
35+
err4 := New("error")
36+
err5 := New("error")
37+
assert.Equal(t, err4, err5)
38+
assert.NotSame(t, err4, err5)
39+
}
40+
41+
func TestUnwrap(t *testing.T) {
42+
err := New("original error 1")
43+
44+
cause := err.Unwrap()
45+
46+
assert.Equal(t, reflect.TypeOf(cause).String(), "*errors.errorString")
47+
assert.Equal(t, "original error 1", cause.Error())
48+
49+
oErr := errors.New("original error 2")
50+
err = Wrap(oErr)
51+
52+
assert.Same(t, oErr, err.Unwrap())
53+
54+
oErr2 := errors.New("original error 3")
55+
err = err.WithCause(oErr2)
56+
assert.Same(t, oErr2, err.Unwrap())
57+
}
58+
59+
func TestErrorsIs(t *testing.T) {
60+
tests := []struct {
61+
name string
62+
err error
63+
}{
64+
{
65+
name: "*errors.errorString",
66+
err: errors.New("dummy error"),
67+
},
68+
{
69+
name: "*DataError",
70+
err: New("dummy error"),
71+
},
72+
}
73+
74+
for _, test := range tests {
75+
t.Run(test.name, func(t *testing.T) {
76+
assert.ErrorIs(t, test.err, test.err)
77+
assert.ErrorIs(t, Wrap(test.err), test.err)
78+
assert.ErrorIs(t, Wrap(test.err).WithField("action", "call"), test.err)
79+
assert.ErrorIs(t, Wrap(test.err).WithField("action", "call").WithField("type", "warning"), test.err)
80+
assert.ErrorIs(t, Wrap(test.err).WithFields(map[string]any{"type": "warning"}), test.err)
81+
})
82+
}
83+
}
84+
85+
func TestErrorsIsWithFields(t *testing.T) {
86+
assert.ErrorIs(t, New("dummy error").WithField("action", "call"), New("dummy error"))
87+
assert.NotErrorIs(t, New("dummy error"), New("dummy error").WithField("action", "call"))
88+
assert.NotErrorIs(t, New("dummy error").WithField("action", "call"), New("dummy error").WithField("action", "send"))
89+
90+
err := New("dummy error").WithField("module", "http")
91+
assert.ErrorIs(t, err.WithField("add", false), err)
92+
assert.NotErrorIs(t, err, err.WithField("add", false))
93+
}

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/gravitton/errors
2+
3+
go 1.24
4+
5+
require github.com/gravitton/assert v0.1.0

0 commit comments

Comments
 (0)