File: csv.go

package info (click to toggle)
fq 0.9.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 106,624 kB
  • sloc: xml: 2,835; makefile: 250; sh: 241; exp: 57; ansic: 21
file content (109 lines) | stat: -rw-r--r-- 2,067 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package csv

import (
	"bytes"
	"embed"
	"encoding/csv"
	"errors"
	"fmt"
	"io"

	"github.com/wader/fq/format"
	"github.com/wader/fq/internal/gojqex"
	"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
//go:embed csv.md
var csvFS embed.FS

func init() {
	interp.RegisterFormat(
		format.CSV,
		&decode.Format{
			Description: "Comma separated values",
			ProbeOrder:  format.ProbeOrderTextFuzzy,
			DecodeFn:    decodeCSV,
			DefaultInArg: format.CSV_In{
				Comma:   ",",
				Comment: "#",
			},
			Functions: []string{"_todisplay"},
		})
	interp.RegisterFS(csvFS)
	interp.RegisterFunc1("_to_csv", toCSV)
}

func decodeCSV(d *decode.D) any {
	var ci format.CSV_In
	d.ArgAs(&ci)

	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.Any{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 := gojqex.Cast[[]any](row)
		if !ok {
			return fmt.Errorf("expected row to be an array, got %s", gojqex.TypeErrorPreview(row))
		}
		vs, ok := gojqex.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", gojqex.TypeErrorPreview(v))
			}
			ss = append(ss, s)
		}
		if err := w.Write(ss); err != nil {
			return err
		}
	}
	w.Flush()

	return b.String()
}