Skip to content

Commit 8095433

Browse files
committed
Adds Err() method to scanner and checks error in Scan()
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.
1 parent da2eb20 commit 8095433

File tree

2 files changed

+39
-7
lines changed

2 files changed

+39
-7
lines changed

scanner.go

+28-6
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type Scanner struct {
5858
tokenDepth int
5959
repeateSpecialChar bool // only '}' can be repeated
6060
prev string
61+
err error
6162
}
6263

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

79+
// Err returns the first non-EOF error that was encountered by the Scanner.
80+
func (s *Scanner) Err() error {
81+
if s.err == io.EOF {
82+
return nil
83+
}
84+
return s.err
85+
}
86+
87+
func (s *Scanner) setErr(err error) {
88+
if s.err == nil || s.err != io.EOF {
89+
s.err = err
90+
}
91+
}
92+
7893
// Scan reads the next token from source and returns it.. It returns io.EOF at the end of the source. Scanner errors are
7994
// returned when encountered.
8095
func (s *Scanner) Scan() (Token, error) { //nolint: funlen, gocognit, gocyclo
@@ -88,21 +103,26 @@ func (s *Scanner) Scan() (Token, error) { //nolint: funlen, gocognit, gocyclo
88103
var r, quote string
89104

90105
for {
106+
if s.err != nil {
107+
return Token{}, s.err
108+
}
109+
91110
switch {
92111
case s.prev != "":
93-
r = s.prev
94-
s.prev = ""
112+
r, s.prev = s.prev, ""
95113
case readNext:
96114
if !s.scanner.Scan() {
97115
if tok.Len() > 0 {
98116
return Token{Text: tok.String(), Line: s.tokenStartLine, IsQuoted: lexState == inQuote}, nil
99117
}
100118

101119
if s.tokenDepth > 0 {
102-
return Token{}, &scannerError{line: s.tokenStartLine, msg: "unexpected end of file, expecting }"}
120+
s.setErr(&scannerError{line: s.tokenStartLine, msg: "unexpected end of file, expecting }"})
121+
return Token{}, s.err
103122
}
104123

105-
return Token{}, io.EOF
124+
s.setErr(io.EOF)
125+
return Token{}, s.err
106126
}
107127

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

186206
// only } can be repeated
187207
if s.repeateSpecialChar && r != "}" {
188-
return Token{}, newScannerErrf(s.tokenStartLine, "unxpected %q", r)
208+
s.setErr(newScannerErrf(s.tokenStartLine, "unxpected %q", r))
209+
return Token{}, s.err
189210
}
190211

191212
s.repeateSpecialChar = true
@@ -196,7 +217,8 @@ func (s *Scanner) Scan() (Token, error) { //nolint: funlen, gocognit, gocyclo
196217
if r == "}" {
197218
s.tokenDepth--
198219
if s.tokenDepth < 0 {
199-
return Token{}, &scannerError{line: s.tokenStartLine, msg: `unexpected "}"`}
220+
s.setErr(&scannerError{line: s.tokenStartLine, msg: `unexpected "}"`})
221+
return Token{}, s.err
200222
}
201223
}
202224

scanner_test.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package crossplane
22

33
import (
4+
"errors"
45
"io"
56
"os"
67
"strings"
@@ -66,7 +67,16 @@ func TestScanner_unhappy(t *testing.T) {
6667

6768
if err != nil {
6869
t.Logf("got error: %v", err)
69-
return
70+
71+
if gotErr := s.Err(); !errors.Is(gotErr, err) {
72+
t.Fatalf("error do not match: have=%+v, want=%+v", gotErr, err)
73+
}
74+
75+
if _, gotErr := s.Scan(); !errors.Is(gotErr, err) {
76+
t.Fatalf("error after scan does not match: have=%+v, want=%+v", gotErr, err)
77+
}
78+
79+
break
7080
}
7181
}
7282
})

0 commit comments

Comments
 (0)