Skip to content

Commit 11af483

Browse files
authored
Merge pull request #292 from noborus/add-text-format
Add Text Format.
2 parents e3c8cb6 + 726e791 commit 11af483

File tree

6 files changed

+206
-2
lines changed

6 files changed

+206
-2
lines changed

cmd/cmd.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,15 @@ func (cli Cli) Run(args []string) int {
124124
flags.IntVar(&inLimitRead, "ilr", 0, "limited number of rows to read.")
125125
flags.StringVar(&inJQuery, "ijq", "", "jq expression string for input(JSON/JSONL only).")
126126
flags.Var(&inNull, "inull", "value(string) to convert to null on input.")
127+
flags.BoolVar(&inRowNumber, "inum", false, "add row number column.")
127128

128129
flags.BoolVar(&inFlag.CSV, "icsv", false, "CSV format for input.")
129130
flags.BoolVar(&inFlag.LTSV, "iltsv", false, "LTSV format for input.")
130131
flags.BoolVar(&inFlag.JSON, "ijson", false, "JSON format for input.")
131132
flags.BoolVar(&inFlag.YAML, "iyaml", false, "YAML format for input.")
132133
flags.BoolVar(&inFlag.TBLN, "itbln", false, "TBLN format for input.")
133134
flags.BoolVar(&inFlag.WIDTH, "iwidth", false, "width specification format for input.")
134-
flags.BoolVar(&inRowNumber, "inum", false, "add row number column.")
135+
flags.BoolVar(&inFlag.TEXT, "itext", false, "text format for input.")
135136

136137
flags.StringVar(&outFile, "out", "", "output file name.")
137138
flags.BoolVar(&outWithoutGuess, "out-without-guess", false, "output without guessing (when using -out).")
@@ -501,6 +502,7 @@ type inputFlag struct {
501502
YAML bool
502503
TBLN bool
503504
WIDTH bool
505+
TEXT bool
504506
}
505507

506508
// inputFormat returns format from flag.
@@ -518,14 +520,16 @@ func inputFormat(i inputFlag) trdsql.Format {
518520
return trdsql.TBLN
519521
case i.WIDTH:
520522
return trdsql.WIDTH
523+
case i.TEXT:
524+
return trdsql.TEXT
521525
default:
522526
return trdsql.GUESS
523527
}
524528
}
525529

526530
func isInFormat(name string) bool {
527531
switch name {
528-
case "ig", "icsv", "iltsv", "ijson", "iyaml", "itbln", "iwidth":
532+
case "ig", "icsv", "iltsv", "ijson", "iyaml", "itbln", "iwidth", "itext":
529533
return true
530534
}
531535
return false

input_text.go

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package trdsql
2+
3+
import (
4+
"bufio"
5+
"io"
6+
"strings"
7+
)
8+
9+
// TextReader provides a reader for text format.
10+
type TextReader struct {
11+
reader *bufio.Reader
12+
num int
13+
maxNum int
14+
}
15+
16+
// NewTextReader returns a new TextReader.
17+
func NewTextReader(reader io.Reader, opts *ReadOpts) (*TextReader, error) {
18+
r := &TextReader{
19+
reader: bufio.NewReader(reader),
20+
}
21+
22+
if opts.InSkip > 0 {
23+
skipRead(r, opts.InSkip)
24+
}
25+
26+
if opts.InLimitRead {
27+
r.maxNum = opts.InPreRead
28+
}
29+
return r, nil
30+
}
31+
32+
// Names returns column names.
33+
func (r *TextReader) Names() ([]string, error) {
34+
return []string{"text"}, nil
35+
}
36+
37+
// Types returns column types.
38+
func (r *TextReader) Types() ([]string, error) {
39+
return []string{"text"}, nil
40+
}
41+
42+
// PreReadRow returns pre-read rows.
43+
func (r *TextReader) PreReadRow() [][]any {
44+
return nil
45+
}
46+
47+
// ReadRow reads a row.
48+
func (r *TextReader) ReadRow([]any) ([]any, error) {
49+
var builder strings.Builder
50+
for {
51+
if r.maxNum > 0 && r.num >= r.maxNum {
52+
return []any{""}, io.EOF
53+
}
54+
line, isPrefix, err := r.reader.ReadLine()
55+
if err != nil {
56+
return []any{""}, err
57+
}
58+
builder.Write(line)
59+
if isPrefix {
60+
continue
61+
}
62+
r.num++
63+
return []any{builder.String()}, nil
64+
}
65+
}

input_text_test.go

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package trdsql
2+
3+
import (
4+
"io"
5+
"path/filepath"
6+
"reflect"
7+
"strings"
8+
"testing"
9+
)
10+
11+
func TestNewTextReader(t *testing.T) {
12+
type args struct {
13+
reader io.Reader
14+
opts *ReadOpts
15+
}
16+
tests := []struct {
17+
name string
18+
args args
19+
}{
20+
{
21+
name: "test1",
22+
args: args{
23+
reader: strings.NewReader("a\nb\nc\n"),
24+
opts: NewReadOpts(),
25+
},
26+
},
27+
}
28+
for _, tt := range tests {
29+
t.Run(tt.name, func(t *testing.T) {
30+
got, err := NewTextReader(tt.args.reader, tt.args.opts)
31+
if err != nil {
32+
t.Fatal(err)
33+
}
34+
names, err := got.Names()
35+
if err != nil {
36+
t.Fatal(err)
37+
}
38+
if !reflect.DeepEqual(names, []string{"text"}) {
39+
t.Errorf("TextReader.Names() != text %v", names)
40+
}
41+
types, err := got.Types()
42+
if err != nil {
43+
t.Fatal(err)
44+
}
45+
if !reflect.DeepEqual(types, []string{"text"}) {
46+
t.Errorf("TextReader.Types() != text %v", types)
47+
}
48+
})
49+
}
50+
}
51+
52+
func TestTextReaderFile(t *testing.T) {
53+
tests := []struct {
54+
name string
55+
fileName string
56+
opts *ReadOpts
57+
want []any
58+
wantErr bool
59+
}{
60+
{
61+
name: "test.csv",
62+
fileName: "test.csv",
63+
opts: NewReadOpts(),
64+
want: []any{"1,Orange"},
65+
wantErr: false,
66+
},
67+
{
68+
name: "test.csv2",
69+
fileName: "test.csv",
70+
opts: &ReadOpts{InSkip: 1},
71+
want: []any{"2,Melon"},
72+
wantErr: false,
73+
},
74+
{
75+
name: "test.csv3",
76+
fileName: "test.csv",
77+
opts: &ReadOpts{InLimitRead: true, InPreRead: 1},
78+
want: []any{"1,Orange"},
79+
},
80+
}
81+
for _, tt := range tests {
82+
t.Run(tt.name, func(t *testing.T) {
83+
file, err := singleFileOpen(filepath.Join(dataDir, tt.fileName))
84+
if err != nil {
85+
t.Error(err)
86+
}
87+
r, err := NewTextReader(file, tt.opts)
88+
if err != nil {
89+
t.Fatal(err)
90+
}
91+
got, err := r.ReadRow(nil)
92+
if (err != nil) != tt.wantErr {
93+
t.Errorf("TextReader.ReadRow() error = %v, wantErr %v", err, tt.wantErr)
94+
return
95+
}
96+
if !reflect.DeepEqual(got, tt.want) {
97+
t.Errorf("TextReader.ReadRow() = %v, want %v", got, tt.want)
98+
}
99+
})
100+
}
101+
}

reader.go

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ var readerFuncs = map[Format]ReaderFunc{
4949
WIDTH: func(reader io.Reader, opts *ReadOpts) (Reader, error) {
5050
return NewGWReader(reader, opts)
5151
},
52+
TEXT: func(reader io.Reader, opts *ReadOpts) (Reader, error) {
53+
return NewTextReader(reader, opts)
54+
},
5255
}
5356

5457
var (

trdsql.go

+3
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ const (
7777
// Format using guesswidth library.
7878
WIDTH
7979

80+
// import
81+
TEXT
82+
8083
// export
8184
// Output as it is.
8285
// Multiple characters can be selected as delimiter.

trdsql_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,34 @@ func TestTBLNRun(t *testing.T) {
554554
}
555555
}
556556

557+
func TestTextRun(t *testing.T) {
558+
testText := [][]string{
559+
{"test.csv", `1,"1,Orange"
560+
2,"2,Melon"
561+
3,"3,Apple"
562+
`},
563+
{"aiu.csv", "1,あ\n2,い\n3,う\n"},
564+
}
565+
outStream := new(bytes.Buffer)
566+
importer := NewImporter(
567+
InFormat(TEXT),
568+
InRowNumber(true),
569+
)
570+
exporter := NewExporter(NewWriter(OutStream(outStream)))
571+
trd := NewTRDSQL(importer, exporter)
572+
for _, c := range testText {
573+
sqlQuery := "SELECT * FROM " + filepath.Join(dataDir, c[0])
574+
err := trd.Exec(sqlQuery)
575+
if err != nil {
576+
t.Errorf("trdsql error %s", err)
577+
}
578+
if outStream.String() != c[1] {
579+
t.Fatalf("trdsql error %s:%s:%s", c[0], c[1], outStream)
580+
}
581+
outStream.Reset()
582+
}
583+
}
584+
557585
func setOutFormatTRDSQL(outFormat Format, outStream io.Writer) *TRDSQL {
558586
importer := NewImporter(
559587
InFormat(GUESS),

0 commit comments

Comments
 (0)