Skip to content

Commit

Permalink
Adds Err() method to scanner and checks error in Scan()
Browse files Browse the repository at this point in the history
Stores the first error encountered by Scan() and checks it to make sure
scanning stops unrecoverably. The Err() method can use used to fetch the
last non-EOF error.
  • Loading branch information
ornj committed Jan 31, 2024
1 parent da2eb20 commit 8095433
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 7 deletions.
34 changes: 28 additions & 6 deletions scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type Scanner struct {
tokenDepth int
repeateSpecialChar bool // only '}' can be repeated
prev string
err error
}

// NewScanner returns a new Scanner to read from r.
Expand All @@ -75,6 +76,20 @@ func NewScanner(r io.Reader) *Scanner {
return s
}

// Err returns the first non-EOF error that was encountered by the Scanner.
func (s *Scanner) Err() error {
if s.err == io.EOF {
return nil
}
return s.err
}

func (s *Scanner) setErr(err error) {
if s.err == nil || s.err != io.EOF {
s.err = err
}
}

// Scan reads the next token from source and returns it.. It returns io.EOF at the end of the source. Scanner errors are
// returned when encountered.
func (s *Scanner) Scan() (Token, error) { //nolint: funlen, gocognit, gocyclo
Expand All @@ -88,21 +103,26 @@ func (s *Scanner) Scan() (Token, error) { //nolint: funlen, gocognit, gocyclo
var r, quote string

for {
if s.err != nil {
return Token{}, s.err
}

switch {
case s.prev != "":
r = s.prev
s.prev = ""
r, s.prev = s.prev, ""
case readNext:
if !s.scanner.Scan() {
if tok.Len() > 0 {
return Token{Text: tok.String(), Line: s.tokenStartLine, IsQuoted: lexState == inQuote}, nil
}

if s.tokenDepth > 0 {
return Token{}, &scannerError{line: s.tokenStartLine, msg: "unexpected end of file, expecting }"}
s.setErr(&scannerError{line: s.tokenStartLine, msg: "unexpected end of file, expecting }"})
return Token{}, s.err
}

return Token{}, io.EOF
s.setErr(io.EOF)
return Token{}, s.err
}

nextRune := s.scanner.Text()
Expand Down Expand Up @@ -185,7 +205,8 @@ func (s *Scanner) Scan() (Token, error) { //nolint: funlen, gocognit, gocyclo

// only } can be repeated
if s.repeateSpecialChar && r != "}" {
return Token{}, newScannerErrf(s.tokenStartLine, "unxpected %q", r)
s.setErr(newScannerErrf(s.tokenStartLine, "unxpected %q", r))
return Token{}, s.err
}

s.repeateSpecialChar = true
Expand All @@ -196,7 +217,8 @@ func (s *Scanner) Scan() (Token, error) { //nolint: funlen, gocognit, gocyclo
if r == "}" {
s.tokenDepth--
if s.tokenDepth < 0 {
return Token{}, &scannerError{line: s.tokenStartLine, msg: `unexpected "}"`}
s.setErr(&scannerError{line: s.tokenStartLine, msg: `unexpected "}"`})
return Token{}, s.err
}
}

Expand Down
12 changes: 11 additions & 1 deletion scanner_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package crossplane

import (
"errors"
"io"
"os"
"strings"
Expand Down Expand Up @@ -66,7 +67,16 @@ func TestScanner_unhappy(t *testing.T) {

if err != nil {
t.Logf("got error: %v", err)
return

if gotErr := s.Err(); !errors.Is(gotErr, err) {
t.Fatalf("error do not match: have=%+v, want=%+v", gotErr, err)
}

if _, gotErr := s.Scan(); !errors.Is(gotErr, err) {
t.Fatalf("error after scan does not match: have=%+v, want=%+v", gotErr, err)
}

break
}
}
})
Expand Down

0 comments on commit 8095433

Please sign in to comment.