-
Notifications
You must be signed in to change notification settings - Fork 0
/
scanner.go
100 lines (93 loc) · 3.55 KB
/
scanner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package transport
import (
"bufio"
"bytes"
"io"
)
// MessageScanner provides a convenient interface for reading messages from any
// [io.Reader]. Successive calls to Scan() will step through the the reader,
// skipping the empty newline between messages. The internal function used to
// split these messages up is [ScanMessages]. This internal split function cannot
// be overridden at this time.
//
// Scanning stops unrecoverably at EOF, the first I/O error encountered, or
// when data is too large to fit into the internal buffer. Much like
// bufio.Scanner, if more control over scanning messages is required (however
// unlikely), it is recommended that users utilize a bufio.Reader in
// conjunction with ScanMessages.
//
// NOTE(bruxisma): It is unlikely that more granular control scanning is
// required by users, as the input for messages comes from [os.Stdin].
// Additionally, rescanning [os.Stdin] is out of scope for this library, and
// indicates that "something wacky" has gone awry with the APT transport method
// protocol.
type MessageScanner struct {
inner *bufio.Scanner
}
// NewMessageScanner will initialize a [bufio.Scanner] internally, call
// [bufio.Scanner.Split] with [ScanMessages] and then return. This ensures that
// the order of operations does not result in a panic when scanning.
func NewMessageScanner(reader io.Reader) *MessageScanner {
scanner := bufio.NewScanner(reader)
scanner.Split(ScanMessages)
return &MessageScanner{scanner}
}
// Scan advances the MessageScanner to the next message, which will then be
// available through the Message method. It returns false when the scan has
// stopped, either by reaching the end of the input or an error. After Scan
// returns false, the Err method will return any error that occurred during
// scanning, unless it was [io.EOF].
//
// NOTE(bruxisma): Scan panics if the split function returns empty slices
// without advancing the input. This is a side effect of uing [bufio.Scanner]
// internally.
func (scanner *MessageScanner) Scan() bool {
return scanner.inner.Scan()
}
// Err returns the first non-EOF error that was encountered by the
// MessageScanner.
func (scanner *MessageScanner) Err() error {
return scanner.inner.Err()
}
// Message returns the most recent Message when scanning, or an error. The
// value returned will NOT be overwritten by subsequent calls to Scan. However,
// this is done at the cost of an allocation, as the internal bytes buffer is
// unmarshalled into the returned Message.
//
// If [MessageScanner.Scan] has not been called, an error is returned.
func (scanner *MessageScanner) Message() (*Message, error) {
message := &Message{}
data := scanner.inner.Bytes()
err := scanner.inner.Err()
if err != nil {
return nil, err
}
if len(data) == 0 {
return nil, ErrMessageScannerNoData
}
if err := message.UnmarshalBinary(data); err != nil {
return nil, err
}
return message, nil
}
// ScanMessages is a SplitFunc function for [bufio.Scanner] that returns the
// entire set of data starting with a status code and ending in a double
// newline.
//
// Unlike [bufio.ScanLines], this function will error if the last line of input
// is not a newline.
func ScanMessages(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
// messages end with two newlines. We remove the empty one so that message
// deserialization is easier.
if idx := bytes.Index(data, []byte("\n\n")); idx >= 0 {
return idx + 2, data[0:idx], nil
}
if atEOF {
return 0, nil, io.ErrUnexpectedEOF
}
// request more data
return 0, nil, nil
}