influxdb/write/csvToProtocolLines.go

88 lines
2.0 KiB
Go

package write
import (
"encoding/csv"
"fmt"
"io"
"strings"
)
// CsvLineError is returned for csv conversion errors
// Line numbers are 1-indexed
type CsvLineError struct {
Line int
Err error
}
func (e CsvLineError) Error() string {
return fmt.Sprintf("line %d: %v", e.Line, e.Err)
}
type lineReader struct {
// csv reading
csv *csv.Reader
table CsvTable
lineNumber int
// reader results
buffer []byte
index int
finished error
}
func (state *lineReader) Read(p []byte) (n int, err error) {
// state1: finished
if state.finished != nil {
return 0, state.finished
}
// state2: some data are in the buffer to copy
if len(state.buffer) > state.index {
// we have remaining bytes to copy
if len(state.buffer)-state.index > len(p) {
// copy a part of the buffer
copy(p, state.buffer[state.index:state.index+len(p)])
state.index += len(p)
return len(p), nil
}
// copy the entire buffer
n = len(state.buffer) - state.index
copy(p[:n], state.buffer[state.index:])
state.buffer = state.buffer[:0]
state.index = 0
return n, nil
}
// state3: fill buffer with data to read from
for {
// Read each record from csv
state.lineNumber++
row, err := state.csv.Read()
if err != nil {
state.finished = err
return state.Read(p)
}
state.csv.FieldsPerRecord = 0 // reset fields because every row can have different count of columns
if state.lineNumber == 1 && len(row) == 1 && strings.HasPrefix(row[0], "sep=") && len(row[0]) > 4 {
// separator specified in the first line
state.csv.Comma = rune(row[0][4])
continue
}
if state.table.AddRow(row) {
buffer, err := state.table.AppendLine(state.buffer, row)
if err != nil {
state.finished = CsvLineError{state.lineNumber, err}
return state.Read(p)
}
state.buffer = append(buffer, '\n')
break
}
}
return state.Read(p)
}
// CsvToProtocolLines transforms csv data into line protocol data
func CsvToProtocolLines(reader io.Reader) io.Reader {
return &lineReader{
csv: csv.NewReader(reader),
}
}