mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-20 22:51:35 +03:00
2831211ae9
* wip: bunch o' linting * bunch more linting
144 lines
2.9 KiB
Go
144 lines
2.9 KiB
Go
// Package htmlw implements a RecordWriter for HTML.
|
|
package htmlw
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"html"
|
|
"io"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/neilotoole/sq/cli/output"
|
|
"github.com/neilotoole/sq/libsq/core/kind"
|
|
|
|
"github.com/neilotoole/sq/libsq/core/errz"
|
|
"github.com/neilotoole/sq/libsq/core/sqlz"
|
|
"github.com/neilotoole/sq/libsq/core/stringz"
|
|
)
|
|
|
|
// RecordWriter implements output.RecordWriter.
|
|
type recordWriter struct {
|
|
recMeta sqlz.RecordMeta
|
|
out io.Writer
|
|
buf *bytes.Buffer
|
|
}
|
|
|
|
// NewRecordWriter an output.RecordWriter for HTML.
|
|
func NewRecordWriter(out io.Writer) output.RecordWriter {
|
|
return &recordWriter{out: out}
|
|
}
|
|
|
|
// Open implements output.RecordWriter.
|
|
func (w *recordWriter) Open(recMeta sqlz.RecordMeta) error {
|
|
w.recMeta = recMeta
|
|
w.buf = &bytes.Buffer{}
|
|
|
|
const header = `<!doctype html>
|
|
<html>
|
|
<head>
|
|
<title>sq output</title>
|
|
</head>
|
|
<body>
|
|
|
|
<table>
|
|
<colgroup>
|
|
`
|
|
|
|
w.buf.WriteString(header)
|
|
for _, field := range recMeta {
|
|
w.buf.WriteString(" <col class=\"kind-")
|
|
w.buf.WriteString(field.Kind().String())
|
|
w.buf.WriteString("\" />\n")
|
|
}
|
|
w.buf.WriteString(" </colgroup>\n <thead>\n <tr>\n")
|
|
for _, field := range recMeta {
|
|
w.buf.WriteString(" <th scope=\"col\">")
|
|
w.buf.WriteString(field.Name())
|
|
w.buf.WriteString("</th>\n")
|
|
}
|
|
w.buf.WriteString(" </tr>\n </thead>\n <tbody>\n")
|
|
return nil
|
|
}
|
|
|
|
// Flush implements output.RecordWriter.
|
|
func (w *recordWriter) Flush() error {
|
|
_, err := w.buf.WriteTo(w.out) // resets buf
|
|
return errz.Err(err)
|
|
}
|
|
|
|
// Close implements output.RecordWriter.
|
|
func (w *recordWriter) Close() error {
|
|
err := w.Flush()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
const footer = ` </tbody>
|
|
</table>
|
|
|
|
</body>
|
|
</html>
|
|
`
|
|
|
|
_, err = w.out.Write([]byte(footer))
|
|
return errz.Err(err)
|
|
}
|
|
|
|
func (w *recordWriter) writeRecord(rec sqlz.Record) error {
|
|
w.buf.WriteString(" <tr>\n")
|
|
|
|
var s string
|
|
for i, field := range rec {
|
|
w.buf.WriteString(" <td>")
|
|
|
|
switch val := field.(type) {
|
|
default:
|
|
s = html.EscapeString(fmt.Sprintf("%v", val))
|
|
// should never happen
|
|
case nil:
|
|
// nil is rendered as empty string, which this cell already is
|
|
case *int64:
|
|
s = strconv.FormatInt(*val, 10)
|
|
case *string:
|
|
s = html.EscapeString(*val)
|
|
case *bool:
|
|
s = strconv.FormatBool(*val)
|
|
case *float64:
|
|
s = stringz.FormatFloat(*val)
|
|
case *[]byte:
|
|
s = base64.StdEncoding.EncodeToString(*val)
|
|
case *time.Time:
|
|
switch w.recMeta[i].Kind() { //nolint:exhaustive
|
|
default:
|
|
s = val.Format(stringz.DatetimeFormat)
|
|
case kind.Time:
|
|
s = val.Format(stringz.TimeFormat)
|
|
case kind.Date:
|
|
s = val.Format(stringz.DateFormat)
|
|
}
|
|
}
|
|
|
|
w.buf.WriteString(s)
|
|
w.buf.WriteString("</td>\n")
|
|
}
|
|
|
|
w.buf.WriteString(" </tr>\n")
|
|
|
|
return nil
|
|
}
|
|
|
|
// WriteRecords implements output.RecordWriter.
|
|
func (w *recordWriter) WriteRecords(recs []sqlz.Record) error {
|
|
var err error
|
|
for _, rec := range recs {
|
|
err = w.writeRecord(rec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|