Skip to content

Commit

Permalink
Merge pull request #155 from itchyny/jq-better-parse-error
Browse files Browse the repository at this point in the history
jq: bump up gojq, better query parse error, handle halt error gracefully
  • Loading branch information
williammartin committed Apr 3, 2024
2 parents 288843f + c617fbc commit a69ba78
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 18 deletions.
10 changes: 5 additions & 5 deletions go.mod
Expand Up @@ -11,13 +11,13 @@ require (
github.com/cli/shurcooL-graphql v0.0.4
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/henvic/httpretty v0.0.6
github.com/itchyny/gojq v0.12.13
github.com/itchyny/gojq v0.12.15
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
github.com/muesli/reflow v0.3.0
github.com/muesli/termenv v0.13.0
github.com/stretchr/testify v1.7.0
github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e
golang.org/x/sys v0.13.0
golang.org/x/sys v0.18.0
golang.org/x/term v0.13.0
golang.org/x/text v0.13.0
gopkg.in/h2non/gock.v1 v1.1.2
Expand All @@ -37,12 +37,12 @@ require (
github.com/kr/pretty v0.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/microcosm-cc/bluemonday v1.0.26 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/yuin/goldmark v1.5.2 // indirect
github.com/yuin/goldmark-emoji v1.0.1 // indirect
golang.org/x/net v0.17.0 // indirect
Expand Down
19 changes: 10 additions & 9 deletions go.sum
Expand Up @@ -35,8 +35,8 @@ github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTx
github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU=
github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4=
github.com/itchyny/gojq v0.12.15 h1:WC1Nxbx4Ifw5U2oQWACYz32JK8G9qxNtHzrvW4KEcqI=
github.com/itchyny/gojq v0.12.15/go.mod h1:uWAHCbCIla1jiNxmeT5/B5mOjSdfkCq6p8vxWg+BM10=
github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
Expand All @@ -53,12 +53,13 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
Expand All @@ -77,8 +78,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
Expand Down Expand Up @@ -112,8 +113,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
Expand Down
28 changes: 28 additions & 0 deletions pkg/jq/jq.go
Expand Up @@ -4,11 +4,13 @@ package jq
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"math"
"os"
"strconv"
"strings"

"github.com/cli/go-gh/v2/pkg/jsonpretty"
"github.com/itchyny/gojq"
Expand All @@ -29,6 +31,14 @@ func Evaluate(input io.Reader, output io.Writer, expr string) error {
func EvaluateFormatted(input io.Reader, output io.Writer, expr string, indent string, colorize bool) error {
query, err := gojq.Parse(expr)
if err != nil {
var e *gojq.ParseError
if errors.As(err, &e) {
str, line, column := getLineColumn(expr, e.Offset-len(e.Token))
return fmt.Errorf(
"failed to parse jq expression (line %d, column %d)\n %s\n %*c %w",
line, column, str, column, '^', err,
)
}
return err
}

Expand Down Expand Up @@ -65,6 +75,10 @@ func EvaluateFormatted(input io.Reader, output io.Writer, expr string, indent st
break
}
if err, isErr := v.(error); isErr {
var e *gojq.HaltError
if errors.As(err, &e) && e.Value() == nil {
break
}
return err
}
if text, e := jsonScalarToString(v); e == nil {
Expand Down Expand Up @@ -129,3 +143,17 @@ func (p prettyEncoder) Encode(v any) error {
}
return jsonpretty.Format(p.w, bytes.NewReader(b), p.indent, true)
}

func getLineColumn(expr string, offset int) (string, int, int) {
for line := 1; ; line++ {
index := strings.Index(expr, "\n")
if index < 0 {
return expr, line, offset + 1
}
if index >= offset {
return expr[:index], line, offset + 1
}
expr = expr[index+1:]
offset -= index + 1
}
}
63 changes: 59 additions & 4 deletions pkg/jq/jq_test.go
Expand Up @@ -19,10 +19,11 @@ func TestEvaluateFormatted(t *testing.T) {
colorize bool
}
tests := []struct {
name string
args args
wantW string
wantErr bool
name string
args args
wantW string
wantErr bool
wantErrMsg string
}{
{
name: "simple",
Expand Down Expand Up @@ -171,13 +172,67 @@ func TestEvaluateFormatted(t *testing.T) {
" \x1b[32m\"bar\"\x1b[m\n" +
"\x1b[1;38m}\x1b[m\n",
},
{
name: "halt function",
args: args{
json: strings.NewReader("{}"),
expr: `1,halt,2`,
},
wantW: "1\n",
},
{
name: "halt_error function",
args: args{
json: strings.NewReader("{}"),
expr: `1,halt_error,2`,
},
wantW: "1\n",
wantErr: true,
wantErrMsg: "halt error: {}",
},
{
name: "invalid one-line query",
args: args{
json: strings.NewReader("{}"),
expr: `[1,2,,3]`,
},
wantErr: true,
wantErrMsg: `failed to parse jq expression (line 1, column 6)
[1,2,,3]
^ unexpected token ","`,
},
{
name: "invalid multi-line query",
args: args{
json: strings.NewReader("{}"),
expr: `[
1,,2
,3]`,
},
wantErr: true,
wantErrMsg: `failed to parse jq expression (line 2, column 5)
1,,2
^ unexpected token ","`,
},
{
name: "invalid unterminated query",
args: args{
json: strings.NewReader("{}"),
expr: `[1,`,
},
wantErr: true,
wantErrMsg: `failed to parse jq expression (line 1, column 4)
[1,
^ unexpected EOF`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &bytes.Buffer{}
err := EvaluateFormatted(tt.args.json, w, tt.args.expr, tt.args.indent, tt.args.colorize)
if tt.wantErr {
assert.Error(t, err)
assert.EqualError(t, err, tt.wantErrMsg)
return
}
assert.NoError(t, err)
Expand Down

0 comments on commit a69ba78

Please sign in to comment.