csv.*csvExport.openFile   A
last analyzed

Complexity

Conditions 5

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 12
nop 0
dl 0
loc 20
rs 9.3333
c 0
b 0
f 0
1
package csv
2
3
import (
4
	"adrianolaselva.github.io/csvql/pkg/exportdata"
5
	"database/sql"
6
	"encoding/csv"
7
	"fmt"
8
	"github.com/schollz/progressbar/v3"
9
	"os"
10
	"path/filepath"
11
)
12
13
const (
14
	fileModeDefault os.FileMode = 0644
15
)
16
17
type csvExport struct {
18
	rows       *sql.Rows
19
	bar        *progressbar.ProgressBar
20
	file       *os.File
21
	exportPath string
22
	columns    []string
23
}
24
25
func NewCsvExport(rows *sql.Rows, exportPath string, bar *progressbar.ProgressBar) exportdata.Export {
26
	return &csvExport{rows: rows, exportPath: exportPath, bar: bar}
27
}
28
29
// Export rows in file
30
func (c *csvExport) Export() error {
31
	if err := c.loadColumns(); err != nil {
32
		return fmt.Errorf("failed to load columns: %w", err)
33
	}
34
35
	if err := c.openFile(); err != nil {
36
		return fmt.Errorf("failed to open file: %w", err)
37
	}
38
39
	w := csv.NewWriter(c.file)
40
	defer w.Flush()
41
42
	if err := w.Write(c.columns); err != nil {
43
		return fmt.Errorf("failed to write headers: %w", err)
44
	}
45
46
	for c.rows.Next() {
47
		_ = c.bar.Add(1)
48
		if err := c.readAndAppendFile(w); err != nil {
49
			return fmt.Errorf("failed to read and append line in file: %w", err)
50
		}
51
	}
52
53
	return nil
54
}
55
56
// readAndAppendFile read line and append in file
57
func (c *csvExport) readAndAppendFile(w *csv.Writer) error {
58
	values := make([]interface{}, len(c.columns))
59
	pointers := make([]interface{}, len(c.columns))
60
	for i := range values {
61
		pointers[i] = &values[i]
62
	}
63
64
	if err := c.rows.Scan(pointers...); err != nil {
65
		return fmt.Errorf("failed to load row: %w", err)
66
	}
67
68
	if err := w.Write(c.convertToStringArray(values)); err != nil {
69
		return fmt.Errorf("failed to write row: %w", err)
70
	}
71
72
	return nil
73
}
74
75
// convertToStringArray convert string array to string array
76
func (c *csvExport) convertToStringArray(records []interface{}) []string {
77
	values := make([]string, 0, len(records))
78
	for _, r := range records {
79
		values = append(values, r.(string))
80
	}
81
82
	return values
83
}
84
85
// Close execute in defer
86
func (c *csvExport) Close() error {
87
	defer func(file *os.File) {
88
		_ = file.Close()
89
	}(c.file)
90
91
	return nil
92
}
93
94
// openFile open file
95
func (c *csvExport) openFile() error {
96
	if _, err := os.Stat(c.exportPath); !os.IsNotExist(err) {
97
		err := os.Remove(c.exportPath)
98
		if err != nil {
99
			return fmt.Errorf("failed to remove file: %w", err)
100
		}
101
	}
102
103
	if err := os.MkdirAll(filepath.Dir(c.exportPath), os.ModePerm); err != nil {
104
		return fmt.Errorf("failed to create path: %w", err)
105
	}
106
107
	file, err := os.OpenFile(c.exportPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, fileModeDefault)
108
	if err != nil {
109
		return fmt.Errorf("failed to open file %s: %w", c.exportPath, err)
110
	}
111
112
	c.file = file
113
114
	return nil
115
}
116
117
// loadColumns load columns
118
func (c *csvExport) loadColumns() error {
119
	columns, err := c.rows.Columns()
120
	if err != nil {
121
		return fmt.Errorf("failed to load columns: %w", err)
122
	}
123
124
	c.columns = columns
125
126
	return nil
127
}
128