sq/cli/output/xlsxw/xlsxw.go
Neil O'Toole 58ccfc9ded
Json driver; refactoring of core packages (#66)
* Type Detector refactor

* json driver impl; refactoring of source.Files reader et al

* working on kind detector

* significant switcheroo of packages

* partway throught refactoring Kind

* major package switcheroo for Kind
2020-08-23 04:42:15 -06:00

111 lines
2.3 KiB
Go

// Package xlsxw implements output writers for Microsoft Excel.
package xlsxw
import (
"io"
"time"
"github.com/neilotoole/sq/libsq/core/kind"
"github.com/neilotoole/sq/libsq/core/stringz"
"github.com/neilotoole/sq/cli/output"
"github.com/tealeg/xlsx/v2"
"github.com/neilotoole/sq/libsq/core/errz"
"github.com/neilotoole/sq/libsq/core/sqlz"
)
type recordWriter struct {
recMeta sqlz.RecordMeta
out io.Writer
header bool
xfile *xlsx.File
sheet *xlsx.Sheet
}
// NewRecordWriter returns an output.RecordWriter instance for XLSX.
func NewRecordWriter(out io.Writer, header bool) output.RecordWriter {
return &recordWriter{out: out, header: header}
}
// Open implements output.RecordWriter.
func (w *recordWriter) Open(recMeta sqlz.RecordMeta) error {
w.recMeta = recMeta
w.xfile = xlsx.NewFile()
sheet, err := w.xfile.AddSheet("data")
if err != nil {
return errz.Wrap(err, "unable to create XLSX sheet")
}
w.sheet = sheet
if w.header {
headerRow := w.sheet.AddRow()
for _, colName := range w.recMeta.Names() {
cell := headerRow.AddCell()
cell.SetString(colName)
}
}
return nil
}
// Flush implements output.RecordWriter.
func (w *recordWriter) Flush() error {
return nil
}
// Close implements output.RecordWriter.
func (w *recordWriter) Close() error {
err := w.xfile.Write(w.out)
if err != nil {
return errz.Wrap(err, "unable to write XLSX")
}
return nil
}
// WriteRecords implements output.RecordWriter.
func (w *recordWriter) WriteRecords(recs []sqlz.Record) error {
for _, rec := range recs {
row := w.sheet.AddRow()
for i, val := range rec {
cell := row.AddCell()
switch val := val.(type) {
case nil:
case *[]byte:
cell.SetValue(*val)
case *string:
cell.SetString(*val)
case *bool:
cell.SetBool(*val)
case *int64:
cell.SetInt64(*val)
case *float64:
cell.SetFloat(*val)
case *time.Time:
switch w.recMeta[i].Kind() {
default:
cell.SetDateTime(*val)
case kind.Date:
cell.SetDate(*val)
case kind.Time:
// TODO: Maybe there's a way of setting a specific
// time (as opposed to date or datetime) value, but
// for now we just use a string.
cell.SetValue(val.Format(stringz.TimeFormat))
}
default:
// should never happen
cell.SetValue(val)
}
}
}
return nil
}