aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/zclconf/go-cty/cty/function/stdlib/csv.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/function/stdlib/csv.go')
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/function/stdlib/csv.go93
1 files changed, 93 insertions, 0 deletions
diff --git a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/csv.go b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/csv.go
new file mode 100644
index 0000000..5070a5a
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/csv.go
@@ -0,0 +1,93 @@
1package stdlib
2
3import (
4 "encoding/csv"
5 "fmt"
6 "io"
7 "strings"
8
9 "github.com/zclconf/go-cty/cty"
10 "github.com/zclconf/go-cty/cty/function"
11)
12
13var CSVDecodeFunc = function.New(&function.Spec{
14 Params: []function.Parameter{
15 {
16 Name: "str",
17 Type: cty.String,
18 },
19 },
20 Type: func(args []cty.Value) (cty.Type, error) {
21 str := args[0]
22 if !str.IsKnown() {
23 return cty.DynamicPseudoType, nil
24 }
25
26 r := strings.NewReader(str.AsString())
27 cr := csv.NewReader(r)
28 headers, err := cr.Read()
29 if err == io.EOF {
30 return cty.DynamicPseudoType, fmt.Errorf("missing header line")
31 }
32 if err != nil {
33 return cty.DynamicPseudoType, err
34 }
35
36 atys := make(map[string]cty.Type, len(headers))
37 for _, name := range headers {
38 if _, exists := atys[name]; exists {
39 return cty.DynamicPseudoType, fmt.Errorf("duplicate column name %q", name)
40 }
41 atys[name] = cty.String
42 }
43 return cty.List(cty.Object(atys)), nil
44 },
45 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
46 ety := retType.ElementType()
47 atys := ety.AttributeTypes()
48 str := args[0]
49 r := strings.NewReader(str.AsString())
50 cr := csv.NewReader(r)
51 cr.FieldsPerRecord = len(atys)
52
53 // Read the header row first, since that'll tell us which indices
54 // map to which attribute names.
55 headers, err := cr.Read()
56 if err != nil {
57 return cty.DynamicVal, err
58 }
59
60 var rows []cty.Value
61 for {
62 cols, err := cr.Read()
63 if err == io.EOF {
64 break
65 }
66 if err != nil {
67 return cty.DynamicVal, err
68 }
69
70 vals := make(map[string]cty.Value, len(cols))
71 for i, str := range cols {
72 name := headers[i]
73 vals[name] = cty.StringVal(str)
74 }
75 rows = append(rows, cty.ObjectVal(vals))
76 }
77
78 if len(rows) == 0 {
79 return cty.ListValEmpty(ety), nil
80 }
81 return cty.ListVal(rows), nil
82 },
83})
84
85// CSVDecode parses the given CSV (RFC 4180) string and, if it is valid,
86// returns a list of objects representing the rows.
87//
88// The result is always a list of some object type. The first row of the
89// input is used to determine the object attributes, and subsequent rows
90// determine the values of those attributes.
91func CSVDecode(str cty.Value) (cty.Value, error) {
92 return CSVDecodeFunc.Call([]cty.Value{str})
93}