blob: 1c7520cadef9b80d22cac290bae36b5dc0e3b66e [file] [log] [blame]
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bufio"
"io"
)
// NamedScanner is a simple struct to pair a name with a Scanner.
type NamedScanner struct {
Name string
Scanner *bufio.Scanner
}
// NamedReader is a simple struct to pair a name with a Reader,
// which will be converted to a Scanner using bufio.NewScanner.
type NamedReader struct {
Name string
Reader io.Reader
}
// MultiScanner scans over multiple bufio.Scanners as if they were a single stream.
// It also keeps track of the name of the current scanner and the line number.
type MultiScanner struct {
scanners []NamedScanner
scannerIdx int
line int
totalLine int
err error
}
// NewMultiScanner creates a new MultiScanner from slice of NamedScanners.
func NewMultiScanner(scanners []NamedScanner) *MultiScanner {
return &MultiScanner{
scanners: scanners,
scannerIdx: -1, // Start before the first scanner
}
}
// MultiScannerFromReaders creates a new MultiScanner from a slice of NamedReaders.
func MultiScannerFromReaders(readers []NamedReader) *MultiScanner {
var scanners []NamedScanner
for _, r := range readers {
scanners = append(scanners, NamedScanner{
Name: r.Name,
Scanner: bufio.NewScanner(r.Reader),
})
}
return NewMultiScanner(scanners)
}
// Scan advances the scanner to the next token, which will then be
// available through the Text method. It returns false when the scan stops,
// 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, except that if it was io.EOF, Err
// will return nil.
func (ms *MultiScanner) Scan() bool {
if ms.scannerIdx == -1 {
ms.scannerIdx = 0
}
for ms.scannerIdx < len(ms.scanners) {
current := ms.scanners[ms.scannerIdx]
if current.Scanner.Scan() {
ms.line++
ms.totalLine++
return true
}
if err := current.Scanner.Err(); err != nil {
ms.err = err
return false
}
// Move to the next scanner
ms.scannerIdx++
ms.line = 0
}
return false
}
// Text returns the most recent token generated by a call to Scan.
func (ms *MultiScanner) Text() string {
if ms.scannerIdx < 0 || ms.scannerIdx >= len(ms.scanners) {
return ""
}
return ms.scanners[ms.scannerIdx].Scanner.Text()
}
// Err returns the first non-EOF error that was encountered by the MultiScanner.
func (ms *MultiScanner) Err() error {
return ms.err
}
// Name returns the name of the current scanner.
func (ms *MultiScanner) Name() string {
if ms.scannerIdx < 0 {
return "<before first>"
}
if ms.scannerIdx >= len(ms.scanners) {
return "<after last>"
}
return ms.scanners[ms.scannerIdx].Name
}
// Line returns the current line number within the current scanner.
func (ms *MultiScanner) Line() int {
return ms.line
}
// TotalLine returns the total number of lines scanned across all scanners.
func (ms *MultiScanner) TotalLine() int {
return ms.totalLine
}