@@ -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,146 @@ 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 (map [string ]interface {})
1437+ var keys []string
1438+ reader := csv .NewReader (strings .NewReader (s ))
1439+ for {
1440+ record , err := reader .Read ()
1441+ if err == io .EOF {
1442+ break
1443+ }
1444+ if err != nil {
1445+ return nil , i .Error (fmt .Sprintf ("failed to parse JSON: %s" , err .Error ()))
1446+ }
1447+
1448+ if len (keys ) == 0 {
1449+ keys = record
1450+ for _ , r := range record {
1451+ json [r ] = []interface {}{}
1452+ }
1453+ } else {
1454+ for i , r := range record {
1455+ k := keys [i ]
1456+ v := json [k ].([]interface {})
1457+ json [k ] = append (v , r )
1458+ }
1459+ }
1460+ }
1461+ return jsonToValue (i , json )
1462+ }
1463+
1464+ func builtinManifestCsv (i * interpreter , arguments []value ) (value , error ) {
1465+ objv := arguments [0 ]
1466+ hv := arguments [1 ]
1467+
1468+ obj , err := i .getObject (objv )
1469+ if err != nil {
1470+ return nil , err
1471+ }
1472+
1473+ var headers []string
1474+ if hv .getType () == nullType {
1475+ // default to all headers
1476+ simpleObj := obj .uncached .(* simpleObject )
1477+ for fieldName := range simpleObj .fields {
1478+ headers = append (headers , fieldName )
1479+ }
1480+ } else {
1481+ // headers are provided
1482+ ha , err := i .getArray (hv )
1483+ if err != nil {
1484+ return nil , err
1485+ }
1486+
1487+ for _ , elem := range ha .elements {
1488+ header , err := i .evaluateString (elem )
1489+ if err != nil {
1490+ return nil , err
1491+ }
1492+ headers = append (headers , header .getGoString ())
1493+ }
1494+ }
1495+
1496+ var buf bytes.Buffer
1497+ w := csv .NewWriter (& buf )
1498+ w .Write (headers )
1499+
1500+ for r := 0 ; ; r ++ {
1501+ record := make ([]string , len (headers ))
1502+ elems := 0
1503+ for c , h := range headers {
1504+ arrv , err := obj .index (i , h )
1505+ if err != nil { // no corresponding column
1506+ // skip to next column
1507+ continue
1508+ }
1509+
1510+ v , err := i .getArray (arrv )
1511+ if err != nil {
1512+ return nil , i .Error ("invalid JSON: not a valid json for CSV" )
1513+ }
1514+
1515+ if r >= len (v .elements ) { // if less elements in record
1516+ continue
1517+ }
1518+ val , err := v .elements [r ].getValue (i )
1519+ if err != nil {
1520+ return nil , err
1521+ }
1522+
1523+ s , err := stringFromValue (i , val )
1524+ if err != nil {
1525+ return nil , err
1526+ }
1527+ record [c ] = s
1528+ elems ++
1529+ }
1530+ if elems == 0 { // No elements in record
1531+ break
1532+ }
1533+ w .Write (record )
1534+ }
1535+
1536+ w .Flush ()
1537+
1538+ return makeValueString (buf .String ()), nil
1539+ }
1540+
1541+ func stringFromValue (i * interpreter , v value ) (string , error ) {
1542+ switch v .getType () {
1543+ case stringType :
1544+ s , err := i .getString (v )
1545+ if err != nil {
1546+ return "" , err
1547+ }
1548+ return s .getGoString (), nil
1549+ case numberType :
1550+ n , err := i .getNumber (v )
1551+ if err != nil {
1552+ return "" , err
1553+ }
1554+ return fmt .Sprint (n .value ), nil
1555+ case booleanType :
1556+ b , err := i .getBoolean (v )
1557+ if err != nil {
1558+ return "" , err
1559+ }
1560+ return fmt .Sprint (b .value ), nil
1561+ case nullType :
1562+ return "" , nil
1563+ default :
1564+ // for functionType, objectType and arrayType
1565+ return "" , i .Error ("invalid string conversion" )
1566+ }
1567+ }
1568+
14281569func jsonEncode (v interface {}) (string , error ) {
14291570 buf := new (bytes.Buffer )
14301571 enc := json .NewEncoder (buf )
@@ -2290,6 +2431,8 @@ var funcBuiltins = buildBuiltinMap([]builtin{
22902431 & unaryBuiltin {name : "parseInt" , function : builtinParseInt , params : ast.Identifiers {"str" }},
22912432 & unaryBuiltin {name : "parseJson" , function : builtinParseJSON , params : ast.Identifiers {"str" }},
22922433 & unaryBuiltin {name : "parseYaml" , function : builtinParseYAML , params : ast.Identifiers {"str" }},
2434+ & unaryBuiltin {name : "parseCsv" , function : builtinParseCSV , params : ast.Identifiers {"str" }},
2435+ & generalBuiltin {name : "manifestCsv" , function : builtinManifestCsv , params : []generalBuiltinParameter {{name : "obj" }, {name : "arr" , defaultValue : & nullValue }}},
22932436 & generalBuiltin {name : "manifestJsonEx" , function : builtinManifestJSONEx , params : []generalBuiltinParameter {{name : "value" }, {name : "indent" },
22942437 {name : "newline" , defaultValue : & valueFlatString {value : []rune ("\n " )}},
22952438 {name : "key_val_sep" , defaultValue : & valueFlatString {value : []rune (": " )}}}},
0 commit comments