Skip to content

Commit

Permalink
feat: implement DebugString() (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmzane authored May 14, 2024
1 parent 15f883e commit 9450f41
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 21 deletions.
60 changes: 45 additions & 15 deletions builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ package queries

import (
"fmt"
"slices"
"strings"
)

type Builder struct {
query strings.Builder
Args []any
counter int
placeholders []rune
query strings.Builder
Args []any
counter int
placeholder rune
}

func (b *Builder) Appendf(format string, args ...any) {
Expand All @@ -21,21 +20,47 @@ func (b *Builder) Appendf(format string, args ...any) {
fmt.Fprintf(&b.query, format, a...)
}

func (b *Builder) String() string {
slices.Sort(b.placeholders)
if len(slices.Compact(b.placeholders)) > 1 {
panic(fmt.Sprintf("queries.Builder: bad query: %s placeholders used", string(b.placeholders)))
func (b *Builder) String() string { return b.string() }

func (b *Builder) DebugString() string {
query := b.string()
for i, arg := range b.Args {
var sarg string
switch arg := arg.(type) {
case string:
sarg = fmt.Sprintf("'%s'", arg)
case fmt.Stringer:
sarg = fmt.Sprintf("'%s'", arg.String())
default:
sarg = fmt.Sprintf("%v", arg)
}

switch b.placeholder {
case '?':
query = strings.Replace(query, "?", sarg, 1)
case '$':
query = strings.Replace(query, fmt.Sprintf("$%d", i+1), sarg, 1)
case '@':
query = strings.Replace(query, fmt.Sprintf("@p%d", i+1), sarg, 1)
default:
panic("unreachable")
}
}
return query
}

s := b.query.String()
if strings.Contains(s, "%!") {
func (b *Builder) string() string {
query := b.query.String()
if strings.Contains(query, "%!") {
// fmt silently recovers panics and writes them to the output.
// we want panics to be loud, so we find and rethrow them.
// see also https://github.com/golang/go/issues/28150.
panic(fmt.Sprintf("queries.Builder: bad query: %s", s))
panic(fmt.Sprintf("queries: bad query: %s", query))
}

return s
if b.placeholder == -1 {
panic("queries: bad query: different placeholders used")
}
return query
}

type argument struct {
Expand All @@ -48,7 +73,12 @@ func (a argument) Format(s fmt.State, verb rune) {
switch verb {
case '?', '$', '@':
a.builder.Args = append(a.builder.Args, a.value)
a.builder.placeholders = append(a.builder.placeholders, verb)
if a.builder.placeholder == 0 {
a.builder.placeholder = verb
}
if a.builder.placeholder != verb {
a.builder.placeholder = -1
}
}

switch verb {
Expand Down
18 changes: 12 additions & 6 deletions builder_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package queries_test

import (
"context"
"testing"

"go-simpler.org/queries"
Expand All @@ -25,27 +26,32 @@ func TestBuilder_placeholders(t *testing.T) {
tests := map[string]struct {
format string
query string
debug string
}{
"?": {
format: "select * from tbl where foo = %? and bar = %? and baz = %?",
query: "select * from tbl where foo = ? and bar = ? and baz = ?",
debug: "select * from tbl where foo = 42 and bar = 'test' and baz = 'context.Background'",
},
"$": {
format: "select * from tbl where foo = %$ and bar = %$ and baz = %$",
query: "select * from tbl where foo = $1 and bar = $2 and baz = $3",
debug: "select * from tbl where foo = 42 and bar = 'test' and baz = 'context.Background'",
},
"@": {
format: "select * from tbl where foo = %@ and bar = %@ and baz = %@",
query: "select * from tbl where foo = @p1 and bar = @p2 and baz = @p3",
debug: "select * from tbl where foo = 42 and bar = 'test' and baz = 'context.Background'",
},
}

for name, tt := range tests {
t.Run(name, func(t *testing.T) {
var qb queries.Builder
qb.Appendf(tt.format, 1, 2, 3)
qb.Appendf(tt.format, 42, "test", context.Background())
assert.Equal[E](t, qb.String(), tt.query)
assert.Equal[E](t, qb.Args, []any{1, 2, 3})
assert.Equal[E](t, qb.Args, []any{42, "test", context.Background()})
assert.Equal[E](t, qb.DebugString(), tt.debug)
})
}
}
Expand All @@ -59,25 +65,25 @@ func TestBuilder_badQuery(t *testing.T) {
appends: func(qb *queries.Builder) {
qb.Appendf("select %d from tbl", "foo")
},
panicMsg: "queries.Builder: bad query: select %!d(string=foo) from tbl",
panicMsg: "queries: bad query: select %!d(string=foo) from tbl",
},
"too few arguments": {
appends: func(qb *queries.Builder) {
qb.Appendf("select %s from tbl")
},
panicMsg: "queries.Builder: bad query: select %!s(MISSING) from tbl",
panicMsg: "queries: bad query: select %!s(MISSING) from tbl",
},
"too many arguments": {
appends: func(qb *queries.Builder) {
qb.Appendf("select %s from tbl", "foo", "bar")
},
panicMsg: "queries.Builder: bad query: select foo from tbl%!(EXTRA queries.argument=bar)",
panicMsg: "queries: bad query: select foo from tbl%!(EXTRA queries.argument=bar)",
},
"different placeholders": {
appends: func(qb *queries.Builder) {
qb.Appendf("select * from tbl where foo = %? and bar = %$ and baz = %@", 1, 2, 3)
},
panicMsg: "queries.Builder: bad query: $?@ placeholders used",
panicMsg: "queries: bad query: different placeholders used",
},
}

Expand Down

0 comments on commit 9450f41

Please sign in to comment.