From 568ee92c71591b1c013ad9b1294b01dcede81a5b Mon Sep 17 00:00:00 2001 From: jonbodner Date: Sun, 22 Feb 2026 22:36:08 -0500 Subject: [PATCH 1/4] Replace `stackerr.Errorf` with `fmt.Errorf` throughout the codebase --- builder.go | 27 +++++++++++++-------------- mapper/extract.go | 6 +++--- mapper/mapper.go | 13 +++++++------ proteus.go | 2 +- proteus_test.go | 6 ++++-- query_mappers_test.go | 2 +- speed/speed.go | 11 +++++------ 7 files changed, 34 insertions(+), 33 deletions(-) diff --git a/builder.go b/builder.go index dbdd44f..bddc337 100644 --- a/builder.go +++ b/builder.go @@ -13,7 +13,6 @@ import ( "database/sql/driver" "github.com/jonbodner/proteus/mapper" - "github.com/jonbodner/stackerr" ) func buildNameOrderMap(paramOrder string, startPos int) map[string]int { @@ -87,7 +86,7 @@ func buildFixedQueryAndParamOrder(ctx context.Context, query string, nameOrderMa if inVar { if curVar.Len() == 0 { //error! must have a something - return nil, nil, stackerr.Errorf("empty variable declaration at position %d", k) + return nil, nil, fmt.Errorf("empty variable declaration at position %d", k) } curVarS := curVar.String() id, err := validIdentifier(ctx, curVarS) @@ -110,13 +109,13 @@ func buildFixedQueryAndParamOrder(ctx context.Context, query string, nameOrderMa paramType := funcType.In(paramPos) if len(path) > 1 { if paramType == nil { - return nil, nil, stackerr.Errorf("query Parameter %s has a path, but the incoming parameter is nil", paramName) + return nil, nil, fmt.Errorf("query Parameter %s has a path, but the incoming parameter is nil", paramName) } switch paramType.Kind() { case reflect.Map, reflect.Struct: //do nothing default: - return nil, nil, stackerr.Errorf("query Parameter %s has a path, but the incoming parameter is not a map or a struct it is %s", paramName, paramType.Kind()) + return nil, nil, fmt.Errorf("query Parameter %s has a path, but the incoming parameter is not a map or a struct it is %s", paramName, paramType.Kind()) } } pathType, err := mapper.ExtractType(ctx, paramType, path) @@ -132,7 +131,7 @@ func buildFixedQueryAndParamOrder(ctx context.Context, query string, nameOrderMa } paramOrder = append(paramOrder, paramInfo{id, paramPos, isSlice}) } else { - return nil, nil, stackerr.Errorf("query Parameter %s cannot be found in the incoming parameters", paramName) + return nil, nil, fmt.Errorf("query Parameter %s cannot be found in the incoming parameters", paramName) } inVar = false @@ -149,7 +148,7 @@ func buildFixedQueryAndParamOrder(ctx context.Context, query string, nameOrderMa } } if inVar { - return nil, nil, stackerr.Errorf("missing a closing : somewhere: %s", query) + return nil, nil, fmt.Errorf("missing a closing : somewhere: %s", query) } queryString := out.String() @@ -233,7 +232,7 @@ func addSlice(sliceName string) string { func validIdentifier(ctx context.Context, curVar string) (string, error) { if strings.Contains(curVar, ";") { - return "", stackerr.Errorf("; is not allowed in an identifier: %s", curVar) + return "", fmt.Errorf("; is not allowed in an identifier: %s", curVar) } curVarB := []byte(curVar) @@ -254,7 +253,7 @@ loop: switch tok { case token.EOF: if first || lastPeriod { - return "", stackerr.Errorf("identifiers cannot be empty or end with a .: %s", curVar) + return "", fmt.Errorf("identifiers cannot be empty or end with a .: %s", curVar) } break loop case token.SEMICOLON: @@ -263,7 +262,7 @@ loop: continue case token.IDENT: if !first && !lastPeriod && !lastFloat { - return "", stackerr.Errorf(". missing between parts of an identifier: %s", curVar) + return "", fmt.Errorf(". missing between parts of an identifier: %s", curVar) } first = false lastPeriod = false @@ -271,7 +270,7 @@ loop: identifier += lit case token.PERIOD: if first || lastPeriod { - return "", stackerr.Errorf("identifier cannot start with . or have two . in a row: %s", curVar) + return "", fmt.Errorf("identifier cannot start with . or have two . in a row: %s", curVar) } lastPeriod = true identifier += "." @@ -283,10 +282,10 @@ loop: first = false continue } - return "", stackerr.Errorf("invalid character found in identifier: %s", curVar) + return "", fmt.Errorf("invalid character found in identifier: %s", curVar) case token.INT: if !dollar || first { - return "", stackerr.Errorf("invalid character found in identifier: %s", curVar) + return "", fmt.Errorf("invalid character found in identifier: %s", curVar) } identifier += lit if dollar { @@ -300,7 +299,7 @@ loop: // returns .0 as the lit value //Only valid for $ notation and array/slice references. if first { - return "", stackerr.Errorf("invalid character found in identifier: %s", curVar) + return "", fmt.Errorf("invalid character found in identifier: %s", curVar) } identifier += lit if dollar { @@ -311,7 +310,7 @@ loop: lastPeriod = true } default: - return "", stackerr.Errorf("invalid character found in identifier: %s", curVar) + return "", fmt.Errorf("invalid character found in identifier: %s", curVar) } } return identifier, nil diff --git a/mapper/extract.go b/mapper/extract.go index 346be27..fc2576e 100644 --- a/mapper/extract.go +++ b/mapper/extract.go @@ -38,7 +38,7 @@ func ExtractType(ctx context.Context, curType reflect.Type, path []string) (refl // handle slices and arrays _, err := strconv.Atoi(path[1]) if err != nil { - return nil, stackerr.Errorf("invalid index: %s :%w", path[1], err) + return nil, fmt.Errorf("invalid index: %s :%w", path[1], err) } return ExtractType(ctx, ss.Elem(), path[1:]) default: @@ -86,10 +86,10 @@ func Extract(ctx context.Context, s any, path []string) (any, error) { // handle slices and arrays pos, err := strconv.Atoi(path[1]) if err != nil { - return nil, stackerr.Errorf("invalid index: %s :%w", path[1], err) + return nil, fmt.Errorf("invalid index: %s :%w", path[1], err) } if pos < 0 || pos >= sv.Len() { - return nil, stackerr.Errorf("invalid index: %s", path[1]) + return nil, fmt.Errorf("invalid index: %s", path[1]) } v := sv.Index(pos) return Extract(ctx, v.Interface(), path[1:]) diff --git a/mapper/mapper.go b/mapper/mapper.go index 5c77bcd..e77346c 100644 --- a/mapper/mapper.go +++ b/mapper/mapper.go @@ -3,6 +3,7 @@ package mapper import ( "context" "database/sql" + "fmt" "log/slog" "reflect" "strings" @@ -27,7 +28,7 @@ func ptrConverter(ctx context.Context, isPtr bool, sType reflect.Type, out refle } k := out.Type().Kind() if (k == reflect.Pointer || k == reflect.Interface) && out.IsNil() { - return nil, stackerr.Errorf("attempting to return nil for non-pointer type %v", sType) + return nil, fmt.Errorf("attempting to return nil for non-pointer type %v", sType) } return out.Interface(), nil } @@ -135,7 +136,7 @@ func buildMap(ctx context.Context, sType reflect.Type, cols []string, vals []any if rv.Elem().Elem().Type().ConvertibleTo(sType.Elem()) { out.SetMapIndex(reflect.ValueOf(v), rv.Elem().Elem().Convert(sType.Elem())) } else { - return out, stackerr.Errorf("unable to assign value %v of type %v to map value of type %v with key %s", rv.Elem().Elem(), rv.Elem().Elem().Type(), sType.Elem(), v) + return out, fmt.Errorf("unable to assign value %v of type %v to map value of type %v with key %s", rv.Elem().Elem(), rv.Elem().Elem().Type(), sType.Elem(), v) } } return out, nil @@ -183,7 +184,7 @@ func buildStructInner(ctx context.Context, sType reflect.Type, out reflect.Value field.Elem().Set(rv.Elem().Elem().Convert(curFieldType.Elem())) } else { slog.ErrorContext(ctx, "can't find the field") - return stackerr.Errorf("unable to assign pointer to value %v of type %v to struct field %s of type %v", rv.Elem().Elem(), rv.Elem().Elem().Type(), sf.name[depth], curFieldType) + return fmt.Errorf("unable to assign pointer to value %v of type %v to struct field %s of type %v", rv.Elem().Elem(), rv.Elem().Elem().Type(), sf.name[depth], curFieldType) } } else { if reflect.PointerTo(curFieldType).Implements(scannerType) { @@ -200,12 +201,12 @@ func buildStructInner(ctx context.Context, sType reflect.Type, out reflect.Value } } else if rv.Elem().IsNil() { slog.ErrorContext(ctx, "attempting to assign nil to non-pointer field") - return stackerr.Errorf("unable to assign nil value to non-pointer struct field %s of type %v", sf.name[depth], curFieldType) + return fmt.Errorf("unable to assign nil value to non-pointer struct field %s of type %v", sf.name[depth], curFieldType) } else if rv.Elem().Elem().Type().ConvertibleTo(curFieldType) { field.Set(rv.Elem().Elem().Convert(curFieldType)) } else { slog.ErrorContext(ctx, "can't find the field") - return stackerr.Errorf("unable to assign value %v of type %v to struct field %s of type %v", rv.Elem().Elem(), rv.Elem().Elem().Type(), sf.name[depth], curFieldType) + return fmt.Errorf("unable to assign value %v of type %v to struct field %s of type %v", rv.Elem().Elem(), rv.Elem().Elem().Type(), sf.name[depth], curFieldType) } } return nil @@ -221,7 +222,7 @@ func buildPrimitive(ctx context.Context, sType reflect.Type, cols []string, vals if rv.Elem().Elem().Type().ConvertibleTo(sType) { out.Set(rv.Elem().Elem().Convert(sType)) } else { - return out, stackerr.Errorf("unable to assign value %v of type %v to return type of type %v", rv.Elem().Elem(), rv.Elem().Elem().Type(), sType) + return out, fmt.Errorf("unable to assign value %v of type %v to return type of type %v", rv.Elem().Elem(), rv.Elem().Elem().Type(), sType) } return out, nil } diff --git a/proteus.go b/proteus.go index c0b4997..a868750 100644 --- a/proteus.go +++ b/proteus.go @@ -347,5 +347,5 @@ func lookupQuery(query string, mappers []QueryMapper) (string, error) { return q, nil } } - return "", stackerr.Errorf("no query found for name %s", name) + return "", fmt.Errorf("no query found for name %s", name) } diff --git a/proteus_test.go b/proteus_test.go index aa73f81..7537acc 100644 --- a/proteus_test.go +++ b/proteus_test.go @@ -10,7 +10,9 @@ import ( "time" + "fmt" "github.com/google/go-cmp/cmp" + pcmp "github.com/jonbodner/proteus/cmp" "github.com/jonbodner/stackerr" @@ -88,7 +90,7 @@ func TestConvertToPositionalParameters(t *testing.T) { reflect.TypeOf(f3), "", nil, - stackerr.Errorf("missing a closing : somewhere: %s", `select * from Product where name=:name: and cost=:cost`), + fmt.Errorf("missing a closing : somewhere: %s", `select * from Product where name=:name: and cost=:cost`), }, //empty :: `select * from Product where name=:: and cost=:cost`: inner{ @@ -104,7 +106,7 @@ func TestConvertToPositionalParameters(t *testing.T) { reflect.TypeOf(f3), "", nil, - stackerr.New("invalid character found in identifier: a,b,c"), + errors.New("invalid character found in identifier: a,b,c"), }, //escaped character (invalid sql, but not the problem at hand) `select * from Pr\:oduct where name=:name: and cost=:cost:`: inner{ diff --git a/query_mappers_test.go b/query_mappers_test.go index 031d965..3a49a16 100644 --- a/query_mappers_test.go +++ b/query_mappers_test.go @@ -30,7 +30,7 @@ func (dd *DummyDB) Exec(query string, args ...any) (sql.Result, error) { func (dd *DummyDB) checkExpectedData(query string, args ...any) error { if dd.pos >= len(dd.Queries) || dd.pos >= len(dd.Args) { - return stackerr.Errorf("Expected at least %d queries and args, only have %d queries and %d args", dd.pos, len(dd.Queries), len(dd.Args)) + return fmt.Errorf("Expected at least %d queries and args, only have %d queries and %d args", dd.pos, len(dd.Queries), len(dd.Args)) } var msg string if dd.Queries[dd.pos] != query { diff --git a/speed/speed.go b/speed/speed.go index 878a7e6..fab3bd3 100644 --- a/speed/speed.go +++ b/speed/speed.go @@ -10,7 +10,6 @@ import ( "time" "github.com/jonbodner/proteus" - "github.com/jonbodner/stackerr" _ "github.com/lib/pq" "github.com/pkg/profile" ) @@ -41,22 +40,22 @@ func SelectProteus(ctx context.Context, db *sql.DB) time.Duration { func validate(i int, p BenchProduct) error { if p.Id != i { - return stackerr.Errorf("should of had id %d, had %d instead", i, p.Id) + return fmt.Errorf("should of had id %d, had %d instead", i, p.Id) } if p.Name != fmt.Sprintf("person%d", i) { - return stackerr.Errorf("should of had person4, had %s instead", p.Name) + return fmt.Errorf("should of had person4, had %s instead", p.Name) } if i%2 == 0 { if p.Cost == nil { - return stackerr.Errorf("cost should have been non-nil") + return fmt.Errorf("cost should have been non-nil") } else { if math.Abs(*p.Cost-1.1*float64(i)) > 0.01 { - return stackerr.Errorf("should have had %f, had %f instead", 1.1*float64(i), *p.Cost) + return fmt.Errorf("should have had %f, had %f instead", 1.1*float64(i), *p.Cost) } } } else { if p.Cost != nil { - return stackerr.Errorf("should have been nil, was %f", *p.Cost) + return fmt.Errorf("should have been nil, was %f", *p.Cost) } } return nil From 08b590dcb782b19452169254975ccae783895f12 Mon Sep 17 00:00:00 2001 From: jonbodner Date: Sun, 22 Feb 2026 22:36:18 -0500 Subject: [PATCH 2/4] Replace `stackerr.New` with `errors.New` throughout the codebase --- mapper/extract.go | 21 +++++++++++---------- mapper/extract_test.go | 5 +++-- mapper/mapper.go | 6 +++--- mapper_test.go | 5 +++-- proteus.go | 30 ++++++++++++++---------------- proteus_function.go | 9 +++++---- proteus_test.go | 5 ++--- query_mappers_test.go | 4 ++-- runner.go | 13 +++++++------ 9 files changed, 50 insertions(+), 48 deletions(-) diff --git a/mapper/extract.go b/mapper/extract.go index fc2576e..d5df39b 100644 --- a/mapper/extract.go +++ b/mapper/extract.go @@ -3,17 +3,18 @@ package mapper import ( "context" "database/sql/driver" + "fmt" "log/slog" "reflect" "strconv" - "github.com/jonbodner/stackerr" + "errors" ) func ExtractType(ctx context.Context, curType reflect.Type, path []string) (reflect.Type, error) { // error case path length == 0 if len(path) == 0 { - return nil, stackerr.New("cannot extract type; no path remaining") + return nil, errors.New("cannot extract type; no path remaining") } ss := fromPtrType(curType) // base case path length == 1 @@ -22,7 +23,7 @@ func ExtractType(ctx context.Context, curType reflect.Type, path []string) (refl } // length > 1, find a match for path[1], and recurse if ss == nil { - return nil, stackerr.New("cannot find the type for the subfield of a nil") + return nil, errors.New("cannot find the type for the subfield of a nil") } switch ss.Kind() { case reflect.Map: @@ -33,7 +34,7 @@ func ExtractType(ctx context.Context, curType reflect.Type, path []string) (refl if f, exists := ss.FieldByName(path[1]); exists { return ExtractType(ctx, f.Type, path[1:]) } - return nil, stackerr.New("cannot find the type; no such field " + path[1]) + return nil, errors.New("cannot find the type; no such field " + path[1]) case reflect.Array, reflect.Slice: // handle slices and arrays _, err := strconv.Atoi(path[1]) @@ -42,14 +43,14 @@ func ExtractType(ctx context.Context, curType reflect.Type, path []string) (refl } return ExtractType(ctx, ss.Elem(), path[1:]) default: - return nil, stackerr.New("cannot find the type for the subfield of anything other than a map, struct, slice, or array") + return nil, errors.New("cannot find the type for the subfield of anything other than a map, struct, slice, or array") } } func Extract(ctx context.Context, s any, path []string) (any, error) { // error case path length == 0 if len(path) == 0 { - return nil, stackerr.New("cannot extract value; no path remaining") + return nil, errors.New("cannot extract value; no path remaining") } // base case path length == 1 if len(path) == 1 { @@ -65,19 +66,19 @@ func Extract(ctx context.Context, s any, path []string) (any, error) { switch sv.Kind() { case reflect.Map: if sv.Type().Key().Kind() != reflect.String { - return nil, stackerr.New("cannot extract value; map does not have a string key") + return nil, errors.New("cannot extract value; map does not have a string key") } slog.DebugContext(ctx, "map extract", "key", path[1], "availableKeys", sv.MapKeys()) v := sv.MapIndex(reflect.ValueOf(path[1])) slog.DebugContext(ctx, "map extract result", "value", v) if !v.IsValid() { - return nil, stackerr.New("cannot extract value; no such map key " + path[1]) + return nil, errors.New("cannot extract value; no such map key " + path[1]) } return Extract(ctx, v.Interface(), path[1:]) case reflect.Struct: //make sure the field exists if _, exists := sv.Type().FieldByName(path[1]); !exists { - return nil, stackerr.New("cannot extract value; no such field " + path[1]) + return nil, errors.New("cannot extract value; no such field " + path[1]) } v := sv.FieldByName(path[1]) @@ -94,7 +95,7 @@ func Extract(ctx context.Context, s any, path []string) (any, error) { v := sv.Index(pos) return Extract(ctx, v.Interface(), path[1:]) default: - return nil, stackerr.New("cannot extract value; only maps and structs can have contained values") + return nil, errors.New("cannot extract value; only maps and structs can have contained values") } } diff --git a/mapper/extract_test.go b/mapper/extract_test.go index eb22917..466f1e3 100644 --- a/mapper/extract_test.go +++ b/mapper/extract_test.go @@ -6,8 +6,9 @@ import ( "reflect" "testing" + "errors" + "github.com/jonbodner/proteus/cmp" - "github.com/jonbodner/stackerr" ) func TestExtractPointer(t *testing.T) { @@ -123,7 +124,7 @@ func TestExtractFail(t *testing.T) { if err == nil { t.Errorf("Expected an error %s, got none", msg) } - eExp := stackerr.New(msg) + eExp := errors.New(msg) if !cmp.Errors(err, eExp) { t.Errorf("Expected error %s, got %s", eExp, err) } diff --git a/mapper/mapper.go b/mapper/mapper.go index e77346c..c09ba23 100644 --- a/mapper/mapper.go +++ b/mapper/mapper.go @@ -8,7 +8,7 @@ import ( "reflect" "strings" - "github.com/jonbodner/stackerr" + "errors" ) func ptrConverter(ctx context.Context, isPtr bool, sType reflect.Type, out reflect.Value, err error) (any, error) { @@ -35,7 +35,7 @@ func ptrConverter(ctx context.Context, isPtr bool, sType reflect.Type, out refle func MakeBuilder(ctx context.Context, sType reflect.Type) (Builder, error) { if sType == nil { - return nil, stackerr.New("sType cannot be nil") + return nil, errors.New("sType cannot be nil") } isPtr := false @@ -52,7 +52,7 @@ func MakeBuilder(ctx context.Context, sType reflect.Type) (Builder, error) { switch sType.Kind() { case reflect.Map: if sType.Key().Kind() != reflect.String { - return nil, stackerr.New("only maps with string keys are supported") + return nil, errors.New("only maps with string keys are supported") } return func(cols []string, vals []any) (any, error) { out, err := buildMap(ctx, sType, cols, vals) diff --git a/mapper_test.go b/mapper_test.go index e670115..2c78b7f 100644 --- a/mapper_test.go +++ b/mapper_test.go @@ -9,9 +9,10 @@ import ( "reflect" "testing" + "errors" + "github.com/jonbodner/proteus/cmp" "github.com/jonbodner/proteus/mapper" - "github.com/jonbodner/stackerr" ) func TestMapRows(t *testing.T) { @@ -22,7 +23,7 @@ func TestMapRows(t *testing.T) { if v != nil { t.Error("Expected nil when passing in nil rows") } - eExp := stackerr.New("rows must be non-nil") + eExp := errors.New("rows must be non-nil") if !cmp.Errors(err, eExp) { t.Errorf("Expected error %s, got %s", eExp, err) } diff --git a/proteus.go b/proteus.go index a868750..7782fb6 100644 --- a/proteus.go +++ b/proteus.go @@ -7,8 +7,6 @@ import ( "log/slog" "reflect" "strings" - - "github.com/jonbodner/stackerr" ) /* @@ -75,12 +73,12 @@ func ShouldBuild(ctx context.Context, dao any, paramAdapter ParamAdapter, mapper daoPointerType := reflect.TypeOf(dao) //must be a pointer to struct if daoPointerType.Kind() != reflect.Pointer { - return stackerr.New("not a pointer") + return errors.New("not a pointer") } daoType := daoPointerType.Elem() //if not a struct, error out if daoType.Kind() != reflect.Struct { - return stackerr.New("not a pointer to struct") + return errors.New("not a pointer to struct") } var out error funcs := make([]reflect.Value, daoType.NumField()) @@ -163,12 +161,12 @@ func Build(dao any, paramAdapter ParamAdapter, mappers ...QueryMapper) error { daoPointerType := reflect.TypeOf(dao) //must be a pointer to struct if daoPointerType.Kind() != reflect.Pointer { - return stackerr.New("not a pointer") + return errors.New("not a pointer") } daoType := daoPointerType.Elem() //if not a struct, error out if daoType.Kind() != reflect.Struct { - return stackerr.New("not a pointer to struct") + return errors.New("not a pointer to struct") } daoPointerValue := reflect.ValueOf(dao) daoValue := reflect.Indirect(daoPointerValue) @@ -251,7 +249,7 @@ var ( func validateFunction(funcType reflect.Type) (bool, error) { //the first parameter is Executor if funcType.NumIn() == 0 { - return false, stackerr.New("need to supply an Executor or Querier parameter") + return false, errors.New("need to supply an Executor or Querier parameter") } var isExec bool var hasContext bool @@ -263,7 +261,7 @@ func validateFunction(funcType reflect.Type) (bool, error) { case fType.Implements(qType): //do nothing isExec is false default: - return false, stackerr.New("first parameter must be of type context.Context, Executor, or Querier") + return false, errors.New("first parameter must be of type context.Context, Executor, or Querier") } start := 1 if hasContext { @@ -274,41 +272,41 @@ func validateFunction(funcType reflect.Type) (bool, error) { case fType.Implements(conQType): //do nothing isExec is false default: - return false, stackerr.New("first parameter must be of type context.Context, Executor, or Querier") + return false, errors.New("first parameter must be of type context.Context, Executor, or Querier") } } //no in parameter can be a channel for i := start; i < funcType.NumIn(); i++ { if funcType.In(i).Kind() == reflect.Chan { - return false, stackerr.New("no input parameter can be a channel") + return false, errors.New("no input parameter can be a channel") } } //has 0, 1, or 2 return values if funcType.NumOut() > 2 { - return false, stackerr.New("must return 0, 1, or 2 values") + return false, errors.New("must return 0, 1, or 2 values") } //if 2 return values, second is error if funcType.NumOut() == 2 { if !funcType.Out(1).Implements(errType) { - return false, stackerr.New("2nd output parameter must be of type error") + return false, errors.New("2nd output parameter must be of type error") } } //if 1 or 2, the 1st param is not a channel (handle map, I guess) if funcType.NumOut() > 0 { if funcType.Out(0).Kind() == reflect.Chan { - return false, stackerr.New("1st output parameter cannot be a channel") + return false, errors.New("1st output parameter cannot be a channel") } if isExec && funcType.Out(0).Kind() != reflect.Int64 && funcType.Out(0) != sqlResultType { - return false, stackerr.New("the 1st output parameter of an Executor must be int64 or sql.Result") + return false, errors.New("the 1st output parameter of an Executor must be int64 or sql.Result") } //sql.Result only useful with executor. if !isExec && funcType.Out(0) == sqlResultType { - return false, stackerr.New("output parameters of type sql.Result must be combined with Executor") + return false, errors.New("output parameters of type sql.Result must be combined with Executor") } } return hasContext, nil @@ -334,7 +332,7 @@ func makeImplementation(ctx context.Context, funcType reflect.Type, query string return makeQuerierImplementation(ctx, funcType, fixedQuery, paramOrder) } //this should be impossible, since we already validated that the first parameter is either an executor or a querier - return nil, stackerr.New("first parameter must be of type Executor or Querier") + return nil, errors.New("first parameter must be of type Executor or Querier") } func lookupQuery(query string, mappers []QueryMapper) (string, error) { diff --git a/proteus_function.go b/proteus_function.go index 5da01ee..135980b 100644 --- a/proteus_function.go +++ b/proteus_function.go @@ -7,8 +7,9 @@ import ( "reflect" "strings" + "errors" + "github.com/jonbodner/proteus/mapper" - "github.com/jonbodner/stackerr" ) type Builder struct { @@ -28,12 +29,12 @@ func (fb Builder) BuildFunction(ctx context.Context, f any, query string, names funcPointerType := reflect.TypeOf(f) //must be a pointer to func if funcPointerType.Kind() != reflect.Pointer { - return stackerr.New("not a pointer") + return errors.New("not a pointer") } funcType := funcPointerType.Elem() //if not a func, error out if funcType.Kind() != reflect.Func { - return stackerr.New("not a pointer to func") + return errors.New("not a pointer to func") } //validate to make sure that the function matches what we expect @@ -107,7 +108,7 @@ func (fb Builder) Query(ctx context.Context, q ContextQuerier, query string, par // make sure that output is a pointer to something outputPointerType := reflect.TypeOf(output) if outputPointerType.Kind() != reflect.Pointer { - return stackerr.New("not a pointer") + return errors.New("not a pointer") } finalQuery, queryArgs, err := fb.setupDynamicQueries(ctx, query, params) diff --git a/proteus_test.go b/proteus_test.go index 7537acc..6b79c39 100644 --- a/proteus_test.go +++ b/proteus_test.go @@ -14,7 +14,6 @@ import ( "github.com/google/go-cmp/cmp" pcmp "github.com/jonbodner/proteus/cmp" - "github.com/jonbodner/stackerr" _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" @@ -98,7 +97,7 @@ func TestConvertToPositionalParameters(t *testing.T) { reflect.TypeOf(f3), "", nil, - stackerr.New("empty variable declaration at position 34"), + errors.New("empty variable declaration at position 34"), }, //invalid identifier `select * from Product where name=:a,b,c: and cost=:cost`: inner{ @@ -165,7 +164,7 @@ func TestValidateFunction(t *testing.T) { if err == nil { t.Fatalf("Expected err") } - eExp := stackerr.New(msg) + eExp := errors.New(msg) if !pcmp.Errors(err, eExp) { t.Errorf("Wrong error expected %s, got %s", eExp, err) } diff --git a/query_mappers_test.go b/query_mappers_test.go index 3a49a16..e6027b5 100644 --- a/query_mappers_test.go +++ b/query_mappers_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/jonbodner/stackerr" + "errors" ) type NoErrType string @@ -49,7 +49,7 @@ func (dd *DummyDB) checkExpectedData(query string, args ...any) error { if len(msg) == 0 { return NoErrType("") } - return stackerr.New(msg) + return errors.New(msg) } func TestMapMapper(t *testing.T) { diff --git a/runner.go b/runner.go index 91126a1..eb2c6c6 100644 --- a/runner.go +++ b/runner.go @@ -8,8 +8,9 @@ import ( "database/sql" + "errors" + "github.com/jonbodner/proteus/mapper" - "github.com/jonbodner/stackerr" ) func buildQueryArgs(ctx context.Context, funcArgs []reflect.Value, paramOrder []paramInfo) ([]any, error) { @@ -150,7 +151,7 @@ func makeExecutorReturnVals(funcType reflect.Type) func(sql.Result, error) []ref // impossible case since validation should happen first, but be safe return func(result sql.Result, err error) []reflect.Value { - impossibleErr := reflect.ValueOf(stackerr.New("should never get here")) + impossibleErr := reflect.ValueOf(errors.New("should never get here")) if sType == sqlResultType { return []reflect.Value{zeroSQLResult, impossibleErr} } @@ -306,13 +307,13 @@ func makeQuerierReturnVals(ctx context.Context, funcType reflect.Type, builder m // impossible case since validation should happen first, but be safe return func(*sql.Rows, error) []reflect.Value { - return []reflect.Value{qZero, reflect.ValueOf(stackerr.New("should never get here!"))} + return []reflect.Value{qZero, reflect.ValueOf(errors.New("should never get here!"))} } } func handleMapping(ctx context.Context, sType reflect.Type, rows *sql.Rows, builder mapper.Builder) (any, error) { if rows == nil { - return nil, stackerr.New("rows must be non-nil") + return nil, errors.New("rows must be non-nil") } defer rows.Close() var val any @@ -349,7 +350,7 @@ func handleMapping(ctx context.Context, sType reflect.Type, rows *sql.Rows, buil func mapRows(ctx context.Context, rows *sql.Rows, builder mapper.Builder) (any, error) { //fmt.Println(sType) if rows == nil { - return nil, stackerr.New("rows must be non-nil") + return nil, errors.New("rows must be non-nil") } if !rows.Next() { if err := rows.Err(); err != nil { @@ -364,7 +365,7 @@ func mapRows(ctx context.Context, rows *sql.Rows, builder mapper.Builder) (any, } if len(cols) == 0 { - return nil, stackerr.New("No values returned from query") + return nil, errors.New("No values returned from query") } vals := make([]any, len(cols)) From 0b364f4f12901ffff9c86997b526dc12d1caaf65 Mon Sep 17 00:00:00 2001 From: jonbodner Date: Sun, 22 Feb 2026 22:36:25 -0500 Subject: [PATCH 3/4] Remove `stackerr` dependency and update `MODERNIZATION.md` --- MODERNIZATION.md | 37 +++++-------------------------------- go.mod | 1 - go.sum | 2 -- 3 files changed, 5 insertions(+), 35 deletions(-) diff --git a/MODERNIZATION.md b/MODERNIZATION.md index 1b398dd..fca8140 100644 --- a/MODERNIZATION.md +++ b/MODERNIZATION.md @@ -16,36 +16,9 @@ Replaced all 8 occurrences of `multierr.Append` with `errors.Join` in `proteus.g --- -## 3. Replace `jonbodner/stackerr` with `fmt.Errorf` and `errors.New` +## ~~3. Replace `jonbodner/stackerr` with `fmt.Errorf` and `errors.New`~~ (DONE) -The `stackerr` package provides stack-trace-annotated errors — a pattern from before Go 1.13 added `%w` wrapping. Modern Go uses: - -- `errors.New("message")` for simple sentinel errors -- `fmt.Errorf("context: %w", err)` for wrapping with context - -**Files affected:** `proteus.go`, `builder.go`, `runner.go`, `mapper/mapper.go`, `mapper/extract.go` - -**Before:** -```go -return stackerr.New("not a pointer") -return stackerr.Errorf("no query found for name %s", name) -``` - -**After:** -```go -return errors.New("not a pointer") -return fmt.Errorf("no query found for name %s", name) -``` - -For places where `stackerr` wraps an existing error, use `%w`: -```go -// Before -return nil, stackerr.Errorf("invalid index: %s :%w", path[1], err) -// After -return nil, fmt.Errorf("invalid index: %s: %w", path[1], err) -``` - -This removes the `jonbodner/stackerr` dependency entirely. +Replaced all `stackerr.New(...)` calls with `errors.New(...)` and all `stackerr.Errorf(...)` calls with `fmt.Errorf(...)` across 11 files (6 production, 5 test). Removed the `jonbodner/stackerr` dependency from `go.mod`. --- @@ -198,10 +171,10 @@ Several dependencies are significantly out of date: | `google/go-cmp` | v0.4.0 | v0.6+ | Test-only dep | | `jonbodner/dbtimer` | 2017 commit | - | Pinned to a 2017 commit hash | | ~~`jonbodner/multierr`~~ | ~~v1.0.0~~ | - | ~~Replaced with `errors.Join` (see #2)~~ *(DONE)* | -| `jonbodner/stackerr` | v1.0.0 | - | Replace with stdlib (see #3) | +| ~~`jonbodner/stackerr`~~ | ~~v1.0.0~~ | - | ~~Replaced with stdlib (see #3)~~ *(DONE)* | | `pkg/profile` | v1.7.0 | - | Only used in `speed/speed.go`; consider removing or moving to a build-tagged file | -After removing `multierr` and `stackerr`, the dependency list shrinks significantly. +After removing `multierr` and `stackerr` (both done), the dependency list has shrunk significantly. --- @@ -391,7 +364,7 @@ If `Build` returns an error, `productDao` will have nil function fields. Subsequ **Medium priority (idiomatic modernization):** - ~~#1 — `interface{}` to `any`~~ *(DONE)* - ~~#2 — Replace `multierr` with `errors.Join`~~ *(DONE)* -- #3 — Replace `stackerr` with stdlib error handling +- ~~#3 — Replace `stackerr` with stdlib error handling~~ *(DONE)* - ~~#4 — Fix slog usage for proper structured logging~~ *(DONE)* - #11 — Update dependencies diff --git a/go.mod b/go.mod index f75d035..e441cda 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/go-sql-driver/mysql v1.5.0 github.com/google/go-cmp v0.6.0 github.com/jonbodner/dbtimer v0.0.0-20170410163237-7002f3758ae1 - github.com/jonbodner/stackerr v1.0.0 github.com/lib/pq v1.10.9 github.com/pkg/profile v1.7.0 github.com/rickar/props v1.0.0 diff --git a/go.sum b/go.sum index 4325c6a..8dd6563 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,6 @@ github.com/google/pprof v0.0.0-20230429030804-905365eefe3e/go.mod h1:79YE0hCXdHa github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/jonbodner/dbtimer v0.0.0-20170410163237-7002f3758ae1 h1:mgFL7UFb88FOlSVgVoIRGJ4yKlkfp8KcXHqy7no+lEU= github.com/jonbodner/dbtimer v0.0.0-20170410163237-7002f3758ae1/go.mod h1:PjOlFbeJKs+4b2CvuN9FFF8Ed8cZ6FHWPb5tLK2QKOM= -github.com/jonbodner/stackerr v1.0.0 h1:rAe+Fh13cfC9IGuKE4YWiVCzwt9zce9Saldpc8fYEIM= -github.com/jonbodner/stackerr v1.0.0/go.mod h1:In1ShJr570PDuDHbYfymEQle+H7PgY9KpT+alyk0nEM= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= From 0b960dcbb0560720cd0e976aa62407f237ec51ce Mon Sep 17 00:00:00 2001 From: jonbodner Date: Sun, 22 Feb 2026 22:43:05 -0500 Subject: [PATCH 4/4] Update error messages for consistency and remove unused Docker version field --- docker-compose.yml | 1 - runner.go | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2d1ff9b..2b37b4a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: "3" services: db: image: "postgres:11" diff --git a/runner.go b/runner.go index eb2c6c6..e46cc07 100644 --- a/runner.go +++ b/runner.go @@ -307,7 +307,7 @@ func makeQuerierReturnVals(ctx context.Context, funcType reflect.Type, builder m // impossible case since validation should happen first, but be safe return func(*sql.Rows, error) []reflect.Value { - return []reflect.Value{qZero, reflect.ValueOf(errors.New("should never get here!"))} + return []reflect.Value{qZero, reflect.ValueOf(errors.New("should never get here"))} } } @@ -365,7 +365,7 @@ func mapRows(ctx context.Context, rows *sql.Rows, builder mapper.Builder) (any, } if len(cols) == 0 { - return nil, errors.New("No values returned from query") + return nil, errors.New("no values returned from query") } vals := make([]any, len(cols))