terraform-provider-google/vendor/github.com/zclconf/go-cty/cty/function/stdlib/csv.go
Paddy 961c878e0d Switch to using Go modules. (#2679)
Switch to using Go modules.

This migrates our vendor.json to use Go 1.11's modules system, and
replaces the vendor folder with the output of go mod vendor.

The vendored code should remain basically the same; I believe some
tree shaking of packages and support scripts/licenses/READMEs/etc.
happened.

This also fixes Travis and our Makefile to no longer use govendor.
2018-12-20 17:22:22 -08:00

94 lines
2.2 KiB
Go

package stdlib
import (
"encoding/csv"
"fmt"
"io"
"strings"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
)
var CSVDecodeFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "str",
Type: cty.String,
},
},
Type: func(args []cty.Value) (cty.Type, error) {
str := args[0]
if !str.IsKnown() {
return cty.DynamicPseudoType, nil
}
r := strings.NewReader(str.AsString())
cr := csv.NewReader(r)
headers, err := cr.Read()
if err == io.EOF {
return cty.DynamicPseudoType, fmt.Errorf("missing header line")
}
if err != nil {
return cty.DynamicPseudoType, err
}
atys := make(map[string]cty.Type, len(headers))
for _, name := range headers {
if _, exists := atys[name]; exists {
return cty.DynamicPseudoType, fmt.Errorf("duplicate column name %q", name)
}
atys[name] = cty.String
}
return cty.List(cty.Object(atys)), nil
},
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
ety := retType.ElementType()
atys := ety.AttributeTypes()
str := args[0]
r := strings.NewReader(str.AsString())
cr := csv.NewReader(r)
cr.FieldsPerRecord = len(atys)
// Read the header row first, since that'll tell us which indices
// map to which attribute names.
headers, err := cr.Read()
if err != nil {
return cty.DynamicVal, err
}
var rows []cty.Value
for {
cols, err := cr.Read()
if err == io.EOF {
break
}
if err != nil {
return cty.DynamicVal, err
}
vals := make(map[string]cty.Value, len(cols))
for i, str := range cols {
name := headers[i]
vals[name] = cty.StringVal(str)
}
rows = append(rows, cty.ObjectVal(vals))
}
if len(rows) == 0 {
return cty.ListValEmpty(ety), nil
}
return cty.ListVal(rows), nil
},
})
// CSVDecode parses the given CSV (RFC 4180) string and, if it is valid,
// returns a list of objects representing the rows.
//
// The result is always a list of some object type. The first row of the
// input is used to determine the object attributes, and subsequent rows
// determine the values of those attributes.
func CSVDecode(str cty.Value) (cty.Value, error) {
return CSVDecodeFunc.Call([]cty.Value{str})
}