@@ -20,6 +20,7 @@ import (
2020 "bytes"
2121 "crypto/md5"
2222 "encoding/base64"
23+ "encoding/csv"
2324 "encoding/hex"
2425 "encoding/json"
2526 "fmt"
@@ -1425,6 +1426,143 @@ func builtinParseYAML(i *interpreter, str value) (value, error) {
14251426 return jsonToValue (i , elems [0 ])
14261427}
14271428
1429+ func builtinParseCSV (i * interpreter , str value ) (value , error ) {
1430+ sval , err := i .getString (str )
1431+ if err != nil {
1432+ return nil , err
1433+ }
1434+ s := sval .getGoString ()
1435+
1436+ json := make ([]interface {}, 0 )
1437+ var keys []string
1438+
1439+ reader := csv .NewReader (strings .NewReader (s ))
1440+ for row := 0 ; ; row ++ {
1441+ record , err := reader .Read ()
1442+ if err == io .EOF {
1443+ break
1444+ }
1445+ if err != nil {
1446+ return nil , i .Error (fmt .Sprintf ("failed to parse JSON: %s" , err .Error ()))
1447+ }
1448+
1449+ if row == 0 { // consider first row as header
1450+ keys = record
1451+ } else {
1452+ j := make (map [string ]interface {})
1453+ for i , k := range keys {
1454+ j [k ] = record [i ]
1455+ }
1456+ json = append (json , j )
1457+ }
1458+ }
1459+ return jsonToValue (i , json )
1460+ }
1461+
1462+ func builtinManifestCsv (i * interpreter , arguments []value ) (value , error ) {
1463+ arrv := arguments [0 ]
1464+ hv := arguments [1 ]
1465+
1466+ arr , err := i .getArray (arrv )
1467+ if err != nil {
1468+ return nil , err
1469+ }
1470+
1471+ var headers []string
1472+ if hv .getType () == nullType {
1473+ if len (arr .elements ) == 0 { // no elements to select headers
1474+ return makeValueString ("" ), nil
1475+ }
1476+
1477+ // default to all headers
1478+ obj , err := i .evaluateObject (arr .elements [0 ])
1479+ if err != nil {
1480+ return nil , err
1481+ }
1482+
1483+ simpleObj := obj .uncached .(* simpleObject )
1484+ for fieldName := range simpleObj .fields {
1485+ headers = append (headers , fieldName )
1486+ }
1487+ } else {
1488+ // headers are provided
1489+ ha , err := i .getArray (hv )
1490+ if err != nil {
1491+ return nil , err
1492+ }
1493+
1494+ for _ , elem := range ha .elements {
1495+ header , err := i .evaluateString (elem )
1496+ if err != nil {
1497+ return nil , err
1498+ }
1499+ headers = append (headers , header .getGoString ())
1500+ }
1501+ }
1502+
1503+ var buf bytes.Buffer
1504+ w := csv .NewWriter (& buf )
1505+
1506+ // Write headers
1507+ w .Write (headers )
1508+
1509+ // Write rest of the rows
1510+ for _ , elem := range arr .elements {
1511+ obj , err := i .evaluateObject (elem )
1512+ if err != nil {
1513+ return nil , err
1514+ }
1515+
1516+ record := make ([]string , len (headers ))
1517+ for c , h := range headers {
1518+ val , err := obj .index (i , h )
1519+ if err != nil { // no corresponding column
1520+ // skip to next column
1521+ continue
1522+ }
1523+
1524+ s , err := stringFromValue (i , val )
1525+ if err != nil {
1526+ return nil , err
1527+ }
1528+ record [c ] = s
1529+ }
1530+ w .Write (record )
1531+ }
1532+
1533+ w .Flush ()
1534+
1535+ return makeValueString (buf .String ()), nil
1536+ }
1537+
1538+ func stringFromValue (i * interpreter , v value ) (string , error ) {
1539+ switch v .getType () {
1540+ case stringType :
1541+ s , err := i .getString (v )
1542+ if err != nil {
1543+ return "" , err
1544+ }
1545+ return s .getGoString (), nil
1546+ case numberType :
1547+ n , err := i .getNumber (v )
1548+ if err != nil {
1549+ return "" , err
1550+ }
1551+ return fmt .Sprint (n .value ), nil
1552+ case booleanType :
1553+ b , err := i .getBoolean (v )
1554+ if err != nil {
1555+ return "" , err
1556+ }
1557+ return fmt .Sprint (b .value ), nil
1558+ case nullType :
1559+ return "" , nil
1560+ default :
1561+ // for functionType, objectType and arrayType
1562+ return "" , i .Error ("invalid string conversion" )
1563+ }
1564+ }
1565+
14281566func jsonEncode (v interface {}) (string , error ) {
14291567 buf := new (bytes.Buffer )
14301568 enc := json .NewEncoder (buf )
@@ -2290,6 +2428,8 @@ var funcBuiltins = buildBuiltinMap([]builtin{
22902428 & unaryBuiltin {name : "parseInt" , function : builtinParseInt , params : ast.Identifiers {"str" }},
22912429 & unaryBuiltin {name : "parseJson" , function : builtinParseJSON , params : ast.Identifiers {"str" }},
22922430 & unaryBuiltin {name : "parseYaml" , function : builtinParseYAML , params : ast.Identifiers {"str" }},
2431+ & unaryBuiltin {name : "parseCsv" , function : builtinParseCSV , params : ast.Identifiers {"str" }},
2432+ & generalBuiltin {name : "manifestCsv" , function : builtinManifestCsv , params : []generalBuiltinParameter {{name : "json" }, {name : "headers" , defaultValue : & nullValue }}},
22932433 & generalBuiltin {name : "manifestJsonEx" , function : builtinManifestJSONEx , params : []generalBuiltinParameter {{name : "value" }, {name : "indent" },
22942434 {name : "newline" , defaultValue : & valueFlatString {value : []rune ("\n " )}},
22952435 {name : "key_val_sep" , defaultValue : & valueFlatString {value : []rune (": " )}}}},
0 commit comments