Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extension support for text format #295

Merged
merged 2 commits into from
Nov 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ func strToFormat(format string) trdsql.Format {
return trdsql.TBLN
case "width":
return trdsql.WIDTH
case "text":
return trdsql.TEXT
default:
return trdsql.GUESS
}
Expand Down Expand Up @@ -361,9 +363,9 @@ func init() {
rootCmd.PersistentFlags().Var(&inNull, "null", "value(string) to convert to null on input.")
rootCmd.PersistentFlags().BoolVarP(&inRowNumber, "row-number", "n", false, "add row number.")

rootCmd.PersistentFlags().StringVarP(&inFormat, "in", "i", "GUESS", "format for input. [CSV|LTSV|JSON|YAML|TBLN|WIDTH]")
rootCmd.PersistentFlags().StringVarP(&inFormat, "in", "i", "GUESS", "format for input. [CSV|LTSV|JSON|YAML|TBLN|WIDTH|TEXT]")
rootCmd.RegisterFlagCompletionFunc("in", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"CSV", "LTSV", "JSON", "YAML", "TBLN", "WIDTH"}, cobra.ShellCompDirectiveDefault
return []string{"CSV", "LTSV", "JSON", "YAML", "TBLN", "WIDTH", "TEXT"}, cobra.ShellCompDirectiveDefault
})
rootCmd.PersistentFlags().StringVar(&outDelimiter, "out-delimiter", ",", "field delimiter for output.")
rootCmd.PersistentFlags().StringVar(&outFile, "out-file", "", "output file name.")
Expand Down
65 changes: 65 additions & 0 deletions input_text.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package trdsql

import (
"bufio"
"io"
"strings"
)

// TextReader provides a reader for text format.
type TextReader struct {
reader *bufio.Reader
num int
maxNum int
}

// NewTextReader returns a new TextReader.
func NewTextReader(reader io.Reader, opts *ReadOpts) (*TextReader, error) {
r := &TextReader{
reader: bufio.NewReader(reader),
}

if opts.InSkip > 0 {
skipRead(r, opts.InSkip)
}

if opts.InLimitRead {
r.maxNum = opts.InPreRead
}
return r, nil
}

// Names returns column names.
func (r *TextReader) Names() ([]string, error) {
return []string{"text"}, nil
}

// Types returns column types.
func (r *TextReader) Types() ([]string, error) {
return []string{"text"}, nil
}

// PreReadRow returns pre-read rows.
func (r *TextReader) PreReadRow() [][]any {
return nil
}

// ReadRow reads a row.
func (r *TextReader) ReadRow([]any) ([]any, error) {
var builder strings.Builder
for {
if r.maxNum > 0 && r.num >= r.maxNum {
return []any{""}, io.EOF
}
line, isPrefix, err := r.reader.ReadLine()
if err != nil {
return []any{""}, err
}
builder.Write(line)
if isPrefix {
continue
}
r.num++
return []any{builder.String()}, nil
}
}
101 changes: 101 additions & 0 deletions input_text_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package trdsql

import (
"io"
"path/filepath"
"reflect"
"strings"
"testing"
)

func TestNewTextReader(t *testing.T) {
type args struct {
reader io.Reader
opts *ReadOpts
}
tests := []struct {
name string
args args
}{
{
name: "test1",
args: args{
reader: strings.NewReader("a\nb\nc\n"),
opts: NewReadOpts(),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NewTextReader(tt.args.reader, tt.args.opts)
if err != nil {
t.Fatal(err)
}
names, err := got.Names()
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(names, []string{"text"}) {
t.Errorf("TextReader.Names() != text %v", names)
}
types, err := got.Types()
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(types, []string{"text"}) {
t.Errorf("TextReader.Types() != text %v", types)
}
})
}
}

func TestTextReaderFile(t *testing.T) {
tests := []struct {
name string
fileName string
opts *ReadOpts
want []any
wantErr bool
}{
{
name: "test.csv",
fileName: "test.csv",
opts: NewReadOpts(),
want: []any{"1,Orange"},
wantErr: false,
},
{
name: "test.csv2",
fileName: "test.csv",
opts: &ReadOpts{InSkip: 1},
want: []any{"2,Melon"},
wantErr: false,
},
{
name: "test.csv3",
fileName: "test.csv",
opts: &ReadOpts{InLimitRead: true, InPreRead: 1},
want: []any{"1,Orange"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
file, err := singleFileOpen(filepath.Join(dataDir, tt.fileName))
if err != nil {
t.Error(err)
}
r, err := NewTextReader(file, tt.opts)
if err != nil {
t.Fatal(err)
}
got, err := r.ReadRow(nil)
if (err != nil) != tt.wantErr {
t.Errorf("TextReader.ReadRow() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("TextReader.ReadRow() = %v, want %v", got, tt.want)
}
})
}
}
4 changes: 4 additions & 0 deletions reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var extToFormat map[string]Format = map[string]Format{
"TSV": TSV,
"PSV": PSV,
"WIDTH": WIDTH,
"TEXT": TEXT,
}

// ReaderFunc is a function that creates a new Reader.
Expand Down Expand Up @@ -49,6 +50,9 @@ var readerFuncs = map[Format]ReaderFunc{
WIDTH: func(reader io.Reader, opts *ReadOpts) (Reader, error) {
return NewGWReader(reader, opts)
},
TEXT: func(reader io.Reader, opts *ReadOpts) (Reader, error) {
return NewTextReader(reader, opts)
},
}

var (
Expand Down
5 changes: 5 additions & 0 deletions trdsql.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ const (
// Format using guesswidth library.
WIDTH

// import
TEXT

// export
// Output as it is.
// Multiple characters can be selected as delimiter.
Expand Down Expand Up @@ -142,6 +145,8 @@ func (f Format) String() string {
return "PSV"
case YAML:
return "YAML"
case TEXT:
return "TEXT"
default:
return "Unknown"
}
Expand Down
29 changes: 29 additions & 0 deletions trdsql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,35 @@ func TestTBLNRun(t *testing.T) {
}
}

func TestTextRun(t *testing.T) {
ctx := context.Background()
testText := [][]string{
{"test.csv", `1,"1,Orange"
2,"2,Melon"
3,"3,Apple"
`},
{"aiu.csv", "1,あ\n2,い\n3,う\n"},
}
outStream := new(bytes.Buffer)
importer := NewImporter(
InFormat(TEXT),
InRowNumber(true),
)
exporter := NewExporter(NewWriter(OutStream(outStream)))
trd := NewTRDSQL(importer, exporter)
for _, c := range testText {
sqlQuery := "SELECT * FROM " + filepath.Join(dataDir, c[0])
err := trd.Exec(ctx, sqlQuery)
if err != nil {
t.Errorf("trdsql error %s", err)
}
if outStream.String() != c[1] {
t.Fatalf("trdsql error %s:%s:%s", c[0], c[1], outStream)
}
outStream.Reset()
}
}

func setOutFormatTRDSQL(outFormat Format, outStream io.Writer) *TRDSQL {
importer := NewImporter(
InFormat(GUESS),
Expand Down