github.com/yandex/pandora@v0.5.32/core/provider/chunk_decoder.go (about)

     1  package provider
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  
     7  	"github.com/pkg/errors"
     8  	"github.com/yandex/pandora/core"
     9  )
    10  
    11  var ErrNoAmmoDecoded = fmt.Errorf("no ammo has been decoded from chunk")
    12  
    13  // ChunkAmmoDecoder accept data chunks that can contain encoded ammo or some meta information that
    14  // changes ChunkAmmoDecoder state and affects next ammo decoding.
    15  // For example, chunks are lines that contains HTTP URI to be transformed to http.Request or
    16  // HTTP header to be added to next decoded http.Requests.
    17  type ChunkAmmoDecoder interface {
    18  	// DecodeChunk accepts chunk of data, than decode it to ammo or change ChunkAmmoDecoder internal
    19  	// state.
    20  	// Returns nil on when ammo was successfully decoded.
    21  	// ErrNoAmmoDecoded MAY be returned, to indicate that chunk was accepted, but ammo were not
    22  	// decoded.
    23  	// Returns other non nil error, on chunk decode fail.
    24  	// Panics if ammo type is not supported.
    25  	DecodeChunk(chunk []byte, ammo core.Ammo) error
    26  }
    27  
    28  func NewScanDecoder(scanner Scanner, decoder ChunkAmmoDecoder) *ScanAmmoDecoder {
    29  	return &ScanAmmoDecoder{scanner: scanner, decoder: decoder}
    30  }
    31  
    32  // Scanner is interface of bufio.Scanner like scanners.
    33  type Scanner interface {
    34  	Scan() bool
    35  	Bytes() []byte
    36  	Err() error
    37  }
    38  
    39  var _ Scanner = &bufio.Scanner{}
    40  
    41  type ScanAmmoDecoder struct {
    42  	chunkCounter int
    43  	scanner      Scanner
    44  	decoder      ChunkAmmoDecoder
    45  }
    46  
    47  var _ AmmoDecoder = &ScanAmmoDecoder{}
    48  
    49  func (d *ScanAmmoDecoder) Decode(ammo core.Ammo) error {
    50  	for {
    51  		if !d.scanner.Scan() {
    52  			return d.scanner.Err()
    53  		}
    54  		chunk := d.scanner.Bytes()
    55  		err := d.decoder.DecodeChunk(chunk, ammo)
    56  		if err == ErrNoAmmoDecoded {
    57  			continue
    58  		}
    59  		if err != nil {
    60  			return errors.Wrapf(err, "chunk %v decode failed", d.chunkCounter)
    61  		}
    62  		d.chunkCounter++
    63  		return nil
    64  	}
    65  
    66  }