Skip to content

Commit

Permalink
export HaltError struct type not interface
Browse files Browse the repository at this point in the history
  • Loading branch information
itchyny committed Mar 30, 2024
1 parent c8171c6 commit bbbbe84
Show file tree
Hide file tree
Showing 6 changed files with 20 additions and 30 deletions.
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,7 @@ func main() {
break
}
if err, ok := v.(error); ok {
if err, ok := err.(gojq.HaltError); ok {
if err.Value() != nil {
log.Fatalln(err)
}
if err, ok := err.(*gojq.HaltError); ok && err.Value() == nil {
break
}
log.Fatalln(err)
Expand All @@ -137,8 +134,8 @@ func main() {
- In either case, you cannot use custom type values as the query input. The type should be `[]any` for an array and `map[string]any` for a map (just like decoded to an `any` using the [encoding/json](https://golang.org/pkg/encoding/json/) package). You can't use `[]int` or `map[string]string`, for example. If you want to query your custom struct, marshal to JSON, unmarshal to `any` and use it as the query input.
- Thirdly, iterate through the results using [`iter.Next() (any, bool)`](https://pkg.go.dev/github.com/itchyny/gojq#Iter). The iterator can emit an error so make sure to handle it. The method returns `true` with results, and `false` when the iterator terminates.
- The return type is not `(any, error)` because the iterator may emit multiple errors. The `jq` and `gojq` commands stop the iteration on the first error, but the library user can choose to stop the iteration on errors, or to continue until it terminates.
- In any case, it is recommended to stop the iteration on [`HaltError`](https://pkg.go.dev/github.com/itchyny/gojq#HaltError), which is emitted on `halt` and `halt_error` functions, although these functions are not commonly used.
If the error value of `HaltError` is `nil`, stop the iteration without handling the error.
- In any case, it is recommended to stop the iteration on [`gojq.HaltError`](https://pkg.go.dev/github.com/itchyny/gojq#HaltError), which is emitted by `halt` and `halt_error` functions, although these functions are rarely used.
The error implements [`gojq.ValueError`](https://pkg.go.dev/github.com/itchyny/gojq#ValueError), and if the error value is `nil`, stop the iteration without handling the error.
Technically speaking, we can fix the iterator to terminate on the halting error, but it does not terminate at the moment.
The `halt` function in jq not only stops the iteration, but also terminates the command execution, even if there are still input values.
So, gojq leaves it up to the library user how to handle the halting error.
Expand Down
2 changes: 1 addition & 1 deletion cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ func (cli *cli) process(iter inputIter, code *gojq.Code) error {
continue
}
if e := cli.printValues(code.Run(v, cli.argvalues...)); e != nil {
if e, ok := e.(gojq.HaltError); ok {
if e, ok := e.(*gojq.HaltError); ok {
if v := e.Value(); v != nil {
if str, ok := v.(string); ok {
cli.errStream.Write([]byte(str))
Expand Down
29 changes: 11 additions & 18 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,6 @@ type ValueError interface {
Value() any
}

// HaltError is an interface for errors of halt and halt_error functions.
// Any HaltError is [ValueError], and if the value is nil, discard the
// error and stop the iteration. Consider a query like "1, halt, 2";
// the first value is 1, and the second value is a HaltError with nil value.
// You might think the iterator should not emit an error this case, but it
// should so that we can recognize the halt error to stop the outer loop
// of iterating input values; echo 1 2 3 | gojq "., halt".
type HaltError interface {
ValueError
isHaltError()
}

type expectedObjectError struct {
v any
}
Expand Down Expand Up @@ -192,22 +180,27 @@ func (err *exitCodeError) ExitCode() int {
return err.code
}

type haltError exitCodeError
// HaltError is an error emitted by halt and halt_error functions.
// It implements [ValueError], and if the value is nil, discard the error
// and stop the iteration. Consider a query like "1, halt, 2";
// the first value is 1, and the second value is a HaltError with nil value.
// You might think the iterator should not emit an error this case, but it
// should so that we can recognize the halt error to stop the outer loop
// of iterating input values; echo 1 2 3 | gojq "., halt".
type HaltError exitCodeError

func (err *haltError) Error() string {
func (err *HaltError) Error() string {
return "halt " + (*exitCodeError)(err).Error()
}

func (err *haltError) Value() any {
func (err *HaltError) Value() any {
return (*exitCodeError)(err).Value()
}

func (err *haltError) ExitCode() int {
func (err *HaltError) ExitCode() int {
return (*exitCodeError)(err).ExitCode()
}

func (err *haltError) isHaltError() {}

type flattenDepthError struct {
v float64
}
Expand Down
2 changes: 1 addition & 1 deletion execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ loop:
case *tryEndError:
err = e.err
break loop
case *breakError, *haltError:
case *breakError, *HaltError:
break loop
case ValueError:
env.pop()
Expand Down
4 changes: 2 additions & 2 deletions func.go
Original file line number Diff line number Diff line change
Expand Up @@ -2099,7 +2099,7 @@ func funcError(v any, args []any) any {
}

func funcHalt(any) any {
return &haltError{nil, 0}
return &HaltError{nil, 0}
}

func funcHaltError(v any, args []any) any {
Expand All @@ -2110,7 +2110,7 @@ func funcHaltError(v any, args []any) any {
return &func0TypeError{"halt_error", args[0]}
}
}
return &haltError{v, code}
return &HaltError{v, code}
}

func toInt(x any) (int, bool) {
Expand Down
4 changes: 2 additions & 2 deletions query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func TestQueryRun_Halt(t *testing.T) {
break
}
if err, ok := v.(error); ok {
if _, ok := err.(gojq.HaltError); ok {
if _, ok := err.(*gojq.HaltError); ok {
break
}
t.Errorf("should emit a halt error but got: %v", err)
Expand All @@ -127,7 +127,7 @@ func TestQueryRun_HaltError(t *testing.T) {
break
}
if err, ok := v.(error); ok {
if _, ok := err.(gojq.HaltError); ok {
if _, ok := err.(*gojq.HaltError); ok {
if expected := "halt error: foo"; err.Error() != expected {
t.Errorf("expected: %v, got: %v", expected, err)
}
Expand Down

0 comments on commit bbbbe84

Please sign in to comment.