@@ -20,6 +20,7 @@ import (
20
20
"bytes"
21
21
"crypto/md5"
22
22
"encoding/base64"
23
+ "encoding/csv"
23
24
"encoding/hex"
24
25
"encoding/json"
25
26
"fmt"
@@ -1425,6 +1426,146 @@ func builtinParseYAML(i *interpreter, str value) (value, error) {
1425
1426
return jsonToValue (i , elems [0 ])
1426
1427
}
1427
1428
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
+
1428
1569
func jsonEncode (v interface {}) (string , error ) {
1429
1570
buf := new (bytes.Buffer )
1430
1571
enc := json .NewEncoder (buf )
@@ -2290,6 +2431,8 @@ var funcBuiltins = buildBuiltinMap([]builtin{
2290
2431
& unaryBuiltin {name : "parseInt" , function : builtinParseInt , params : ast.Identifiers {"str" }},
2291
2432
& unaryBuiltin {name : "parseJson" , function : builtinParseJSON , params : ast.Identifiers {"str" }},
2292
2433
& 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 }}},
2293
2436
& generalBuiltin {name : "manifestJsonEx" , function : builtinManifestJSONEx , params : []generalBuiltinParameter {{name : "value" }, {name : "indent" },
2294
2437
{name : "newline" , defaultValue : & valueFlatString {value : []rune ("\n " )}},
2295
2438
{name : "key_val_sep" , defaultValue : & valueFlatString {value : []rune (": " )}}}},
0 commit comments