1
1
mirror of https://github.com/wader/fq.git synced 2024-12-23 13:22:58 +03:00
fq/format/csv/csv.go
Mattias Wadman cae288e6be format,intepr: Refactor json, yaml, etc into formats also move out related functions
json, yaml, toml, xml, html, csv are now normal formats and most of them also particiate
in probing (not html and csv).

Also fixes a bunch of bugs in to/fromxml, to/fromjq etc.
2022-07-23 21:48:45 +02:00

107 lines
2.0 KiB
Go

package csv
import (
"bytes"
"embed"
"encoding/csv"
"errors"
"fmt"
"io"
"github.com/wader/fq/format"
"github.com/wader/fq/internal/gojqextra"
"github.com/wader/fq/pkg/bitio"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/interp"
"github.com/wader/fq/pkg/scalar"
)
//go:embed csv.jq
var csvFS embed.FS
func init() {
interp.RegisterFormat(decode.Format{
Name: format.CSV,
Description: "Comma separated values",
ProbeOrder: format.ProbeOrderText,
DecodeFn: decodeCSV,
DecodeInArg: format.CSVLIn{
Comma: ",",
Comment: "#",
},
Functions: []string{"_todisplay"},
Files: csvFS,
})
interp.RegisterFunc1("_tocsv", toCSV)
}
func decodeCSV(d *decode.D, in any) any {
ci, _ := in.(format.CSVLIn)
var rvs []any
br := d.RawLen(d.Len())
r := csv.NewReader(bitio.NewIOReader(br))
r.TrimLeadingSpace = true
r.LazyQuotes = true
if ci.Comma != "" {
r.Comma = rune(ci.Comma[0])
}
if ci.Comment != "" {
r.Comment = rune(ci.Comment[0])
}
for {
r, err := r.Read()
if errors.Is(err, io.EOF) {
break
} else if err != nil {
return err
}
var vs []any
for _, s := range r {
vs = append(vs, s)
}
rvs = append(rvs, vs)
}
d.Value.V = &scalar.S{Actual: rvs}
d.Value.Range.Len = d.Len()
return nil
}
type ToCSVOpts struct {
Comma string
}
func toCSV(_ *interp.Interp, c []any, opts ToCSVOpts) any {
b := &bytes.Buffer{}
w := csv.NewWriter(b)
if opts.Comma != "" {
w.Comma = rune(opts.Comma[0])
}
for _, row := range c {
rs, ok := gojqextra.Cast[[]any](row)
if !ok {
return fmt.Errorf("expected row to be an array, got %s", gojqextra.TypeErrorPreview(row))
}
vs, ok := gojqextra.NormalizeToStrings(rs).([]any)
if !ok {
panic("not array")
}
var ss []string
for _, v := range vs {
s, ok := v.(string)
if !ok {
return fmt.Errorf("expected row record to be scalars, got %s", gojqextra.TypeErrorPreview(v))
}
ss = append(ss, s)
}
if err := w.Write(ss); err != nil {
return err
}
}
w.Flush()
return b.String()
}