Skip to content

Commit 48b4a0c

Browse files
authored
cmd: added new output formats. fixes #82. (#93)
Now you can change the output format to 2 new ones: json and csv. - Added json support - Added csv support - Now the table output format is called 'pretty'. It is the default format.
1 parent 0df31d4 commit 48b4a0c

File tree

9 files changed

+283
-16
lines changed

9 files changed

+283
-16
lines changed

Diff for: cmd/gitql/query.go

+22-16
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,18 @@ import (
99

1010
"github.com/gitql/gitql"
1111
gitqlgit "github.com/gitql/gitql/git"
12+
"github.com/gitql/gitql/internal/format"
1213
"github.com/gitql/gitql/sql"
1314

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

1818
type CmdQuery struct {
1919
cmd
2020

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

@@ -86,34 +87,39 @@ func (c *CmdQuery) executeQuery() error {
8687
return err
8788
}
8889

89-
c.printQuery(schema, iter)
90-
91-
return nil
90+
return c.printQuery(schema, iter)
9291
}
9392

94-
func (c *CmdQuery) printQuery(schema sql.Schema, iter sql.RowIter) {
95-
w := tablewriter.NewWriter(os.Stdout)
93+
func (c *CmdQuery) printQuery(schema sql.Schema, iter sql.RowIter) error {
94+
f, err := format.NewFormat(c.Format, os.Stdout)
95+
if err != nil {
96+
return err
97+
}
98+
9699
headers := []string{}
97100
for _, f := range schema {
98101
headers = append(headers, f.Name)
99102
}
100-
w.SetHeader(headers)
103+
104+
if err := f.WriteHeader(headers); err != nil {
105+
return err
106+
}
107+
101108
for {
102109
row, err := iter.Next()
103110
if err == io.EOF {
104111
break
105112
}
106113
if err != nil {
107-
fmt.Printf("Error: %v\n", err)
108-
return
114+
return err
109115
}
110-
rowStrings := []string{}
111-
for _, v := range row.Fields() {
112-
rowStrings = append(rowStrings, fmt.Sprintf("%v", v))
116+
117+
if err := f.Write(row.Fields()); err != nil {
118+
return err
113119
}
114-
w.Append(rowStrings)
115120
}
116-
w.Render()
121+
122+
return f.Close()
117123
}
118124

119125
func findDotGitFolder(path string) (string, error) {

Diff for: internal/format/common.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package format
2+
3+
import (
4+
"fmt"
5+
"io"
6+
)
7+
8+
type Format interface {
9+
WriteHeader(headers []string) error
10+
Write(line []interface{}) error
11+
Close() error
12+
}
13+
14+
func NewFormat(id string, w io.Writer) (Format, error) {
15+
switch id {
16+
case "pretty":
17+
return NewPrettyFormat(w), nil
18+
case "csv":
19+
return NewCsvFormat(w), nil
20+
case "json":
21+
return NewJsonFormat(w), nil
22+
default:
23+
return nil, fmt.Errorf("format not supported: %v", id)
24+
}
25+
}

Diff for: internal/format/common_test.go

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package format
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestNewFormat_InvalidId(t *testing.T) {
11+
assert := assert.New(t)
12+
13+
f, err := NewFormat("INVALID", bytes.NewBuffer(nil))
14+
assert.Nil(f)
15+
assert.NotNil(err)
16+
}
17+
18+
func testNewFormat(id string, t *testing.T) {
19+
assert := assert.New(t)
20+
21+
w := bytes.NewBuffer(nil)
22+
f, err := NewFormat(id, w)
23+
assert.Nil(err)
24+
assert.NotNil(f)
25+
}
26+
27+
func testFormat(fs *formatSpec, writer *bytes.Buffer, t *testing.T) {
28+
assert := assert.New(t)
29+
30+
err := fs.Format.WriteHeader(fs.Headers)
31+
assert.Nil(err)
32+
for _, l := range fs.Lines {
33+
err := fs.Format.Write(l)
34+
assert.Nil(err)
35+
}
36+
err = fs.Format.Close()
37+
assert.Nil(err)
38+
39+
assert.Equal(fs.Result, writer.String())
40+
41+
writer.Reset()
42+
}
43+
44+
type formatSpec struct {
45+
Headers []string
46+
Lines [][]interface{}
47+
Format Format
48+
Result string
49+
}

Diff for: internal/format/csv.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package format
2+
3+
import (
4+
"encoding/csv"
5+
"fmt"
6+
"io"
7+
)
8+
9+
type CsvFormat struct {
10+
cw *csv.Writer
11+
}
12+
13+
func NewCsvFormat(w io.Writer) *CsvFormat {
14+
return &CsvFormat{
15+
cw: csv.NewWriter(w),
16+
}
17+
}
18+
19+
func (cf *CsvFormat) WriteHeader(headers []string) error {
20+
return cf.cw.Write(headers)
21+
}
22+
23+
func (cf *CsvFormat) Write(line []interface{}) error {
24+
rowStrings := []string{}
25+
for _, v := range line {
26+
rowStrings = append(rowStrings, fmt.Sprintf("%v", v))
27+
}
28+
29+
return cf.cw.Write(rowStrings)
30+
}
31+
32+
func (cf *CsvFormat) Close() error {
33+
cf.cw.Flush()
34+
35+
return nil
36+
}

Diff for: internal/format/csv_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package format
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func TestNewCsvFormat(t *testing.T) {
9+
w := bytes.NewBuffer(nil)
10+
testFormat(&formatSpec{
11+
Format: NewCsvFormat(w),
12+
Result: "a,b,c\na1,b1,c1\n",
13+
Headers: []string{"a", "b", "c"},
14+
Lines: [][]interface{}{
15+
[]interface{}{
16+
"a1", "b1", "c1",
17+
},
18+
},
19+
}, w, t)
20+
}
21+
22+
func TestNewFormat_Csv(t *testing.T) {
23+
testNewFormat("csv", t)
24+
}

Diff for: internal/format/json.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package format
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
)
7+
8+
type JsonFormat struct {
9+
je *json.Encoder
10+
keys []string
11+
}
12+
13+
func NewJsonFormat(w io.Writer) *JsonFormat {
14+
return &JsonFormat{
15+
je: json.NewEncoder(w),
16+
}
17+
}
18+
19+
func (cf *JsonFormat) WriteHeader(headers []string) error {
20+
cf.keys = headers
21+
22+
return nil
23+
}
24+
25+
func (cf *JsonFormat) Write(line []interface{}) error {
26+
j := make(map[string]interface{})
27+
for i, k := range cf.keys {
28+
j[k] = line[i]
29+
}
30+
31+
return cf.je.Encode(j)
32+
}
33+
34+
func (cf *JsonFormat) Close() error {
35+
return nil
36+
}

Diff for: internal/format/json_test.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package format
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func TestNewJsonFormat(t *testing.T) {
9+
w := bytes.NewBuffer(nil)
10+
testFormat(&formatSpec{
11+
Format: NewJsonFormat(w),
12+
Result: "{\"a\":\"a1\",\"b\":\"b1\",\"c\":\"c1\"}\n",
13+
Headers: []string{"a", "b", "c"},
14+
Lines: [][]interface{}{
15+
[]interface{}{
16+
"a1", "b1", "c1",
17+
},
18+
},
19+
}, w, t)
20+
}
21+
22+
23+
func TestNewFormat_Json(t *testing.T) {
24+
testNewFormat("json", t)
25+
}

Diff for: internal/format/pretty.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package format
2+
3+
import (
4+
"fmt"
5+
"io"
6+
7+
"github.com/olekukonko/tablewriter"
8+
)
9+
10+
type PrettyFormat struct {
11+
tw *tablewriter.Table
12+
}
13+
14+
func NewPrettyFormat(w io.Writer) *PrettyFormat {
15+
return &PrettyFormat{
16+
tw: tablewriter.NewWriter(w),
17+
}
18+
}
19+
20+
func (pf *PrettyFormat) WriteHeader(headers []string) error {
21+
pf.tw.SetHeader(headers)
22+
23+
return nil
24+
}
25+
26+
func (pf *PrettyFormat) Write(line []interface{}) error {
27+
rowStrings := []string{}
28+
for _, v := range line {
29+
rowStrings = append(rowStrings, fmt.Sprintf("%v", v))
30+
}
31+
pf.tw.Append(rowStrings)
32+
33+
return nil
34+
}
35+
36+
func (pf *PrettyFormat) Close() error {
37+
pf.tw.Render()
38+
39+
return nil
40+
}

Diff for: internal/format/pretty_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package format
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func TestNewPrettyFormat(t *testing.T) {
9+
w := bytes.NewBuffer(nil)
10+
testFormat(&formatSpec{
11+
Format: NewPrettyFormat(w),
12+
Result: "+----+----+----+\n| A | B | C |" +
13+
"\n+----+----+----+\n| a1 | b1 | c1 |" +
14+
"\n+----+----+----+\n",
15+
Headers: []string{"a", "b", "c"},
16+
Lines: [][]interface{}{
17+
[]interface{}{
18+
"a1", "b1", "c1",
19+
},
20+
},
21+
}, w, t)
22+
}
23+
24+
func TestNewFormat_Pretty(t *testing.T) {
25+
testNewFormat("pretty", t)
26+
}

0 commit comments

Comments
 (0)