1package input
2
3import (
4	"bufio"
5	"io"
6	"os"
7	"strconv"
8	"strings"
9
10	"miller/src/cliutil"
11	"miller/src/lib"
12	"miller/src/types"
13)
14
15type RecordReaderNIDX struct {
16	// TODO: use the parameterization
17	ifs string
18	ips string
19}
20
21func NewRecordReaderNIDX(readerOptions *cliutil.TReaderOptions) *RecordReaderNIDX {
22	return &RecordReaderNIDX{
23		ifs: readerOptions.IFS,
24		ips: readerOptions.IPS,
25	}
26}
27
28func (this *RecordReaderNIDX) Read(
29	filenames []string,
30	context types.Context,
31	inputChannel chan<- *types.RecordAndContext,
32	errorChannel chan error,
33) {
34	if filenames != nil { // nil for mlr -n
35		if len(filenames) == 0 { // read from stdin
36			handle := os.Stdin
37			this.processHandle(handle, "(stdin)", &context, inputChannel, errorChannel)
38		} else {
39			for _, filename := range filenames {
40				handle, err := os.Open(filename)
41				if err != nil {
42					errorChannel <- err
43				} else {
44					this.processHandle(handle, filename, &context, inputChannel, errorChannel)
45					handle.Close()
46				}
47			}
48		}
49	}
50	inputChannel <- types.NewEndOfStreamMarker(&context)
51}
52
53func (this *RecordReaderNIDX) processHandle(
54	handle *os.File,
55	filename string,
56	context *types.Context,
57	inputChannel chan<- *types.RecordAndContext,
58	errorChannel chan error,
59) {
60	context.UpdateForStartOfFile(filename)
61
62	lineReader := bufio.NewReader(handle)
63	eof := false
64
65	for !eof {
66		line, err := lineReader.ReadString('\n') // TODO: auto-detect
67		if err == io.EOF {
68			err = nil
69			eof = true
70		} else if err != nil {
71			errorChannel <- err
72		} else {
73			// This is how to do a chomp:
74			line = strings.TrimRight(line, "\n")
75			record := recordFromNIDXLine(&line, &this.ifs)
76
77			context.UpdateForInputRecord()
78			inputChannel <- types.NewRecordAndContext(
79				record,
80				context,
81			)
82		}
83	}
84}
85
86// ----------------------------------------------------------------
87func recordFromNIDXLine(
88	line *string,
89	ifs *string,
90) *types.Mlrmap {
91	record := types.NewMlrmap()
92	values := lib.SplitString(*line, *ifs) // TODO: repifs ...
93	var i int = 0
94	for _, value := range values {
95		i++
96		key := strconv.Itoa(i)
97		mval := types.MlrvalPointerFromInferredType(value)
98		record.PutReference(key, mval)
99	}
100	return record
101}
102