Skip to content

Commit

Permalink
Implement CsvWriter
Browse files Browse the repository at this point in the history
  • Loading branch information
rnixik committed Jul 28, 2018
1 parent da123a0 commit 4f119a3
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -11,3 +11,4 @@ nohup.log
run.bat
stations.ini
result.sql
result.csv
87 changes: 87 additions & 0 deletions csv_writer.go
@@ -0,0 +1,87 @@
package main

import (
"fmt"
"strings"
)

type CsvWriter struct {
fw FileWriter
dstFile string
dstDir string
delimiter string
}

func NewCsvWriter(fw FileWriter, dstFile, dstDir, delimiter string) *CsvWriter {
return &CsvWriter{
fw,
dstFile,
dstDir,
delimiter,
}
}

func (w *CsvWriter) WriteDDL(tableName string, ddl string) (err error) {
return
}

func (w *CsvWriter) WriteRows(tableName string, columns []string, rows []*map[string]interface{}) (err error) {
f, err := w.fw.getFileHandler(w.getFilename(tableName))
if err != nil {
return err
}
defer f.Close()
columnsNames := make([]string, 0)
for _, column := range columns {
columnsNames = append(columnsNames, escapeCsvString(column))
}
_, err = f.WriteString(strings.Join(columnsNames, w.delimiter) + "\r\n")
if err != nil {
return fmt.Errorf("Error at writing header to file: %s", err)
}
for _, row := range rows {
values := make([]string, 0)
for _, field := range columns {
v := (*row)[field]
value := ""
switch typedValue := v.(type) {
case int:
value = fmt.Sprintf("%d", typedValue)
break
case int64:
value = fmt.Sprintf("%d", typedValue)
break
case float64:
value = fmt.Sprintf("%f", typedValue)
break
case string:
value = escapeCsvString(typedValue)
break
case []uint8:
value = escapeCsvString(fmt.Sprintf("%s", typedValue))
case nil:
value = "NULL"
default:
value = "UNDEFINED"
}
values = append(values, value)
}
_, err = f.WriteString(strings.Join(values, w.delimiter) + "\r\n")
if err != nil {
return fmt.Errorf("Error at writing rows to file: %s", err)
}
}
return
}

func (w *CsvWriter) getFilename(tableName string) (filename string) {
if w.dstDir != "" {
return w.dstDir + "/" + tableName + ".csv"
}
return w.dstFile
}

func escapeCsvString(str string) string {
str = strings.Replace(str, "\"", "\"\"", -1)
return "\"" + str + "\""
}
69 changes: 69 additions & 0 deletions csv_writer_test.go
@@ -0,0 +1,69 @@
package main

import "testing"

func TestCsvWriterWriteRows(t *testing.T) {
fw := NewTestFileWriter()
writer := NewCsvWriter(fw, "result.csv", "", ",")
rows := make([]*map[string]interface{}, 0)
rows = append(rows, &map[string]interface{}{
"name": "one",
"title": "t\"wo",
"id": int(123),
"value": int64(456),
"amount": 1.23,
"chars": []uint8{0x26, 0x23, 0x29},
"nulled": nil,
"strange": uintptr(1),
})
rows = append(rows, &map[string]interface{}{
"name": "four",
"title": "five",
"id": int(789),
"value": int64(345),
"amount": 2.23,
"chars": []uint8{0x23, 0x23, 0x29},
"nulled": nil,
"strange": uintptr(1),
})

writer.WriteRows("some_table", []string{"name", "title", "id", "value", "amount", "chars", "nulled", "strange"}, rows)
result := fw.getContents("result.csv")
expected := "\"name\",\"title\",\"id\",\"value\",\"amount\",\"chars\",\"nulled\",\"strange\"\r\n"
expected += "\"one\",\"t\"\"wo\",123,456,1.230000,\"&#)\",NULL,UNDEFINED\r\n"
expected += "\"four\",\"five\",789,345,2.230000,\"##)\",NULL,UNDEFINED\r\n"
if expected != result {
t.Errorf("Expected:\n%sGot:\n%s", expected, result)
}
}

func TestCsvWriterWriteDDL(t *testing.T) {
fw := NewTestFileWriter()
writer := NewCsvWriter(fw, "result.csv", "", ",")
err := writer.WriteDDL("some_table", "")
if err != nil {
t.Errorf("Unexpected error: ", err)
}
}

func TestCsvWriterWriteRowsFileWriteError(t *testing.T) {
fw := &TestFileErrorWriter{}
writer := NewCsvWriter(fw, "result.csv", "", ",")
rows := make([]*map[string]interface{}, 0)
rows = append(rows, &map[string]interface{}{"name": "one"})
err := writer.WriteRows("some_table", []string{"name"}, rows)
if err == nil {
t.Errorf("Expected file writer error, but got nil ")
}
}

func TestCsvWriterWriteRowsFileHandlerError(t *testing.T) {
fw := &TestFileHandlerErrorWriter{}
writer := NewCsvWriter(fw, "result.csv", "", ",")
rows := make([]*map[string]interface{}, 0)
rows = append(rows, &map[string]interface{}{"name": "one"})
err := writer.WriteRows("some_table", []string{"name"}, rows)
if err == nil {
t.Errorf("Expected write error, but got nil ")
}
}
5 changes: 3 additions & 2 deletions main.go
Expand Up @@ -15,13 +15,14 @@ func dbConnect(conset *ConnectionSettings) (db *sqlx.DB, err error) {
func main() {
configFile := flag.String("config", ".env", "source label file")
format := flag.String("format", "sql", "Output format: sql, csv, simple")
dstFile := flag.String("file", "result.sql", "Filename for single output file")
csvDelimiter := flag.String("csv-delimiter", ",", "Delimiter for csv format")
dstFile := flag.String("file", "", "Filename for single output file")
dstDir := flag.String("dir", "", "Output directory for multiple output files")
flag.Parse()

fw := NewOsFileWriter()

err := Run(dbConnect, flag.Args(), *configFile, *format, fw, *dstFile, *dstDir)
err := Run(dbConnect, flag.Args(), *configFile, *format, fw, *dstFile, *dstDir, *csvDelimiter)
if err != nil {
fmt.Println(err)
os.Exit(1)
Expand Down
3 changes: 3 additions & 0 deletions query.go
Expand Up @@ -88,6 +88,9 @@ func (q *Query) selectAndWrite(db *sqlx.DB, writer DataWriter, combined bool) (e
return err
}
err = writer.WriteRows("combined", q.getAllColumns(), resultsMaps)
if err != nil {
return err
}
} else {
var query string
for i, qt := range q.tables {
Expand Down
13 changes: 12 additions & 1 deletion run.go
Expand Up @@ -6,7 +6,7 @@ import (
"os"
)

func Run(dbConnect dbConnector, argsTail []string, configFile string, format string, fw FileWriter, dstFile string, dstDir string) (err error) {
func Run(dbConnect dbConnector, argsTail []string, configFile string, format string, fw FileWriter, dstFile string, dstDir string, csvDelimiter string) (err error) {
if len(argsTail) != 2 && len(argsTail) != 3 {
showHelp()
return
Expand Down Expand Up @@ -34,7 +34,18 @@ func Run(dbConnect dbConnector, argsTail []string, configFile string, format str
writer = &SimpleWriter{}
if format == "sql" {
combined = false
if dstFile == "" && dstDir == "" {
dstFile = "result.sql"
}
writer = NewSqlWriter(fw, dstFile, dstDir)
} else if format == "csv" {
if dstFile == "" && dstDir == "" {
dstFile = "result.csv"
}
if dstDir != "" {
combined = false
}
writer = NewCsvWriter(fw, dstFile, dstDir, csvDelimiter)
}

err = query.QueryResult(dbConnect, conset, writer, combined)
Expand Down
10 changes: 5 additions & 5 deletions run_test.go
Expand Up @@ -13,7 +13,7 @@ func TestRunErrorArguments(t *testing.T) {
dbConnect := func(conset *ConnectionSettings) (db *sqlx.DB, err error) {
return nil, nil
}
err := Run(dbConnect, []string{}, "", "", NewTestFileWriter(), "", "")
err := Run(dbConnect, []string{}, "", "", NewTestFileWriter(), "", "", ",")
if err != nil {
t.Errorf("Expected help, but got error: %s", err)
return
Expand Down Expand Up @@ -41,7 +41,7 @@ func TestRun(t *testing.T) {
WithArgs(1, 2).
WillReturnRows(sqlmock.NewRows([]string{"id"}))

err = Run(dbConnectMock, []string{"some_table:id", "1-2"}, ".env.example", "sql", NewTestFileWriter(), "test_example.sql", "")
err = Run(dbConnectMock, []string{"some_table:id", "1-2"}, ".env.example", "sql", NewTestFileWriter(), "test_example.sql", "", ",")
os.Setenv("DB_NAME", "")
if err != nil {
t.Errorf("Unexpected error: %s", err)
Expand All @@ -63,7 +63,7 @@ func TestRunDbError(t *testing.T) {

mock.ExpectQuery("DESCRIBE `some_table`").WillReturnError(fmt.Errorf("Some DB error"))

err = Run(dbConnectMock, []string{"some_table:id", "1-2"}, ".env.example", "sql", NewTestFileWriter(), "test_example.sql", "")
err = Run(dbConnectMock, []string{"some_table:id", "1-2"}, ".env.example", "sql", NewTestFileWriter(), "test_example.sql", "", ",")
os.Setenv("DB_NAME", "")
if err == nil {
t.Errorf("Expected error, but got nil")
Expand All @@ -76,7 +76,7 @@ func TestRunConfigReadError(t *testing.T) {
return nil, nil
}
os.Setenv("DB_NAME", "")
err := Run(dbConnect, []string{"some_table:id", "1-2"}, "not_existing_file", "", NewTestFileWriter(), "", "")
err := Run(dbConnect, []string{"some_table:id", "1-2"}, "not_existing_file", "", NewTestFileWriter(), "", "", ",")
os.Setenv("DB_NAME", "")
if err == nil {
t.Errorf("Expected error, but got nil")
Expand All @@ -89,7 +89,7 @@ func TestRunParseError(t *testing.T) {
return nil, nil
}
os.Setenv("DB_NAME", "")
err := Run(dbConnect, []string{"", "", ""}, ".env.example", "", NewTestFileWriter(), "", "")
err := Run(dbConnect, []string{"", "", ""}, ".env.example", "", NewTestFileWriter(), "", "", ",")
os.Setenv("DB_NAME", "")
if err == nil {
t.Errorf("Expected error, but got nil")
Expand Down

0 comments on commit 4f119a3

Please sign in to comment.