@@ -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,143 @@ 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 ([]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
+
1428
1566
func jsonEncode (v interface {}) (string , error ) {
1429
1567
buf := new (bytes.Buffer )
1430
1568
enc := json .NewEncoder (buf )
@@ -2290,6 +2428,8 @@ var funcBuiltins = buildBuiltinMap([]builtin{
2290
2428
& unaryBuiltin {name : "parseInt" , function : builtinParseInt , params : ast.Identifiers {"str" }},
2291
2429
& unaryBuiltin {name : "parseJson" , function : builtinParseJSON , params : ast.Identifiers {"str" }},
2292
2430
& 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 }}},
2293
2433
& generalBuiltin {name : "manifestJsonEx" , function : builtinManifestJSONEx , params : []generalBuiltinParameter {{name : "value" }, {name : "indent" },
2294
2434
{name : "newline" , defaultValue : & valueFlatString {value : []rune ("\n " )}},
2295
2435
{name : "key_val_sep" , defaultValue : & valueFlatString {value : []rune (": " )}}}},
0 commit comments