Skip to content

cmd: added new output formats #93

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

Merged
merged 1 commit into from
Jan 13, 2017
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
38 changes: 22 additions & 16 deletions cmd/gitql/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ import (

"github.com/gitql/gitql"
gitqlgit "github.com/gitql/gitql/git"
"github.com/gitql/gitql/internal/format"
"github.com/gitql/gitql/sql"

"github.com/olekukonko/tablewriter"
"gopkg.in/src-d/go-git.v4"
)

type CmdQuery struct {
cmd

Path string `short:"p" long:"path" description:"Path where the git repository is located"`
Args struct {
Path string `short:"p" long:"path" description:"Path where the git repository is located"`
Format string `short:"f" long:"format" default:"pretty" description:"Ouptut format. Formats supported: pretty, csv, json."`
Args struct {
SQL string `positional-arg-name:"sql" required:"true" description:"SQL query to execute"`
} `positional-args:"yes"`

Expand Down Expand Up @@ -86,34 +87,39 @@ func (c *CmdQuery) executeQuery() error {
return err
}

c.printQuery(schema, iter)

return nil
return c.printQuery(schema, iter)
}

func (c *CmdQuery) printQuery(schema sql.Schema, iter sql.RowIter) {
w := tablewriter.NewWriter(os.Stdout)
func (c *CmdQuery) printQuery(schema sql.Schema, iter sql.RowIter) error {
f, err := format.NewFormat(c.Format, os.Stdout)
if err != nil {
return err
}

headers := []string{}
for _, f := range schema {
headers = append(headers, f.Name)
}
w.SetHeader(headers)

if err := f.WriteHeader(headers); err != nil {
return err
}

for {
row, err := iter.Next()
if err == io.EOF {
break
}
if err != nil {
fmt.Printf("Error: %v\n", err)
return
return err
}
rowStrings := []string{}
for _, v := range row.Fields() {
rowStrings = append(rowStrings, fmt.Sprintf("%v", v))

if err := f.Write(row.Fields()); err != nil {
return err
}
w.Append(rowStrings)
}
w.Render()

return f.Close()
}

func findDotGitFolder(path string) (string, error) {
Expand Down
25 changes: 25 additions & 0 deletions internal/format/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package format

import (
"fmt"
"io"
)

type Format interface {
WriteHeader(headers []string) error
Write(line []interface{}) error
Close() error
}

func NewFormat(id string, w io.Writer) (Format, error) {
switch id {
case "pretty":
return NewPrettyFormat(w), nil
case "csv":
return NewCsvFormat(w), nil
case "json":
return NewJsonFormat(w), nil
default:
return nil, fmt.Errorf("format not supported: %v", id)
}
}
49 changes: 49 additions & 0 deletions internal/format/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package format

import (
"bytes"
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewFormat_InvalidId(t *testing.T) {
assert := assert.New(t)

f, err := NewFormat("INVALID", bytes.NewBuffer(nil))
assert.Nil(f)
assert.NotNil(err)
}

func testNewFormat(id string, t *testing.T) {
assert := assert.New(t)

w := bytes.NewBuffer(nil)
f, err := NewFormat(id, w)
assert.Nil(err)
assert.NotNil(f)
}

func testFormat(fs *formatSpec, writer *bytes.Buffer, t *testing.T) {
assert := assert.New(t)

err := fs.Format.WriteHeader(fs.Headers)
assert.Nil(err)
for _, l := range fs.Lines {
err := fs.Format.Write(l)
assert.Nil(err)
}
err = fs.Format.Close()
assert.Nil(err)

assert.Equal(fs.Result, writer.String())

writer.Reset()
}

type formatSpec struct {
Headers []string
Lines [][]interface{}
Format Format
Result string
}
36 changes: 36 additions & 0 deletions internal/format/csv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package format

import (
"encoding/csv"
"fmt"
"io"
)

type CsvFormat struct {
cw *csv.Writer
}

func NewCsvFormat(w io.Writer) *CsvFormat {
return &CsvFormat{
cw: csv.NewWriter(w),
}
}

func (cf *CsvFormat) WriteHeader(headers []string) error {
return cf.cw.Write(headers)
}

func (cf *CsvFormat) Write(line []interface{}) error {
rowStrings := []string{}
for _, v := range line {
rowStrings = append(rowStrings, fmt.Sprintf("%v", v))
}

return cf.cw.Write(rowStrings)
}

func (cf *CsvFormat) Close() error {
cf.cw.Flush()

return nil
}
24 changes: 24 additions & 0 deletions internal/format/csv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package format

import (
"bytes"
"testing"
)

func TestNewCsvFormat(t *testing.T) {
w := bytes.NewBuffer(nil)
testFormat(&formatSpec{
Format: NewCsvFormat(w),
Result: "a,b,c\na1,b1,c1\n",
Headers: []string{"a", "b", "c"},
Lines: [][]interface{}{
[]interface{}{
"a1", "b1", "c1",
},
},
}, w, t)
}

func TestNewFormat_Csv(t *testing.T) {
testNewFormat("csv", t)
}
36 changes: 36 additions & 0 deletions internal/format/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package format

import (
"encoding/json"
"io"
)

type JsonFormat struct {
je *json.Encoder
keys []string
}

func NewJsonFormat(w io.Writer) *JsonFormat {
return &JsonFormat{
je: json.NewEncoder(w),
}
}

func (cf *JsonFormat) WriteHeader(headers []string) error {
cf.keys = headers

return nil
}

func (cf *JsonFormat) Write(line []interface{}) error {
j := make(map[string]interface{})
for i, k := range cf.keys {
j[k] = line[i]
}

return cf.je.Encode(j)
}

func (cf *JsonFormat) Close() error {
return nil
}
25 changes: 25 additions & 0 deletions internal/format/json_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package format

import (
"bytes"
"testing"
)

func TestNewJsonFormat(t *testing.T) {
w := bytes.NewBuffer(nil)
testFormat(&formatSpec{
Format: NewJsonFormat(w),
Result: "{\"a\":\"a1\",\"b\":\"b1\",\"c\":\"c1\"}\n",
Headers: []string{"a", "b", "c"},
Lines: [][]interface{}{
[]interface{}{
"a1", "b1", "c1",
},
},
}, w, t)
}


func TestNewFormat_Json(t *testing.T) {
testNewFormat("json", t)
}
40 changes: 40 additions & 0 deletions internal/format/pretty.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package format

import (
"fmt"
"io"

"github.com/olekukonko/tablewriter"
)

type PrettyFormat struct {
tw *tablewriter.Table
}

func NewPrettyFormat(w io.Writer) *PrettyFormat {
return &PrettyFormat{
tw: tablewriter.NewWriter(w),
}
}

func (pf *PrettyFormat) WriteHeader(headers []string) error {
pf.tw.SetHeader(headers)

return nil
}

func (pf *PrettyFormat) Write(line []interface{}) error {
rowStrings := []string{}
for _, v := range line {
rowStrings = append(rowStrings, fmt.Sprintf("%v", v))
}
pf.tw.Append(rowStrings)

return nil
}

func (pf *PrettyFormat) Close() error {
pf.tw.Render()

return nil
}
26 changes: 26 additions & 0 deletions internal/format/pretty_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package format

import (
"bytes"
"testing"
)

func TestNewPrettyFormat(t *testing.T) {
w := bytes.NewBuffer(nil)
testFormat(&formatSpec{
Format: NewPrettyFormat(w),
Result: "+----+----+----+\n| A | B | C |" +
"\n+----+----+----+\n| a1 | b1 | c1 |" +
"\n+----+----+----+\n",
Headers: []string{"a", "b", "c"},
Lines: [][]interface{}{
[]interface{}{
"a1", "b1", "c1",
},
},
}, w, t)
}

func TestNewFormat_Pretty(t *testing.T) {
testNewFormat("pretty", t)
}