Source: types/results-wrapper.js

"use strict";

const rust = require("../../index");
const Uuid = require("./uuid");
const TimeUuid = require("./time-uuid");
const Duration = require("./duration");
const LocalTime = require("./local-time");
const InetAddress = require("./inet-address");
const LocalDate = require("./local-date");
const { bigintToLong } = require("../new-utils");
const Row = require("./row");
const Tuple = require("./tuple");

/**
 * Maps the value returned from the Rust driver into expected JS object.
 *
 * This function accepts the following kinds of arguments:
 *   1. `Value` ->  When can correctly handle the value based on the type;
 *   2. `[Type, Value]` ->  In case of ambiguity, rust side provides tuple of (CqlType, Value).
 *      Specifically, array based types will always be returned in this form.
 *
 * See rust.CqlValueWrapper, for more information
 * @param {rust.CqlValueWrapper} field
 * @returns {any}
 */
function getCqlObject(field) {
    switch (true) {
        case field === null:
            return null;
        case field instanceof rust.LocalDateWrapper:
            return LocalDate.fromRust(field);
        case field instanceof rust.DurationWrapper:
            return Duration.fromRust(field);
        case field instanceof rust.InetAddressWrapper:
            return InetAddress.fromRust(field);
        case field instanceof rust.LocalTimeWrapper:
            return LocalTime.fromRust(field);
    }

    if (Array.isArray(field)) {
        let res;
        let value = field[1];
        switch (field[0]) {
            case rust.CqlType.BigInt:
                return bigintToLong(value);
            case rust.CqlType.Counter:
                return value;
            case rust.CqlType.Timestamp:
                // Currently only values inside Date safe range are supported
                // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#the_epoch_timestamps_and_invalid_date
                // The same problem exists in Datastax driver. This probably should be fixed at some point,
                // but it's unlikely someone will need timestamps almost 300.000 years into the future.
                return new Date(Number(value));
            case rust.CqlType.Map:
                res = {};
                for (const keyValuePair of value) {
                    res[getCqlObject(keyValuePair[0])] = getCqlObject(
                        keyValuePair[1],
                    );
                }
                return res;
            case rust.CqlType.Timeuuid:
                return TimeUuid.fromRust(value);
            case rust.CqlType.Tuple:
                return Tuple.fromArray(
                    value.map((e) =>
                        e === null ? undefined : getCqlObject(e),
                    ),
                );
            case rust.CqlType.Uuid:
                return Uuid.fromRust(value);
            case rust.CqlType.Set:
            case rust.CqlType.List:
                return value.map((v) => getCqlObject(v));
            default:
                throw new Error("Unexpected type");
        }
    }
    return field;
}

/**
 * Simple way of getting results from rust driver.
 * Call the driver O(columns * rows) times
 * @param {rust.QueryResultWrapper} result
 * @returns {Array<Row> | undefined} Returns array of rows if ResultWrapper has any, and undefined otherwise
 */
function getRowsFromResultsWrapper(result) {
    let rustRows = result.getRows();
    if (rustRows == null) {
        // Empty results are treated as undefined
        return undefined;
    }

    let colNames = result.getColumnsNames();
    let rows = [];

    for (let i = 0; i < rustRows.length; i++) {
        let cols = rustRows[i];
        let collectedRow = new Row(colNames);
        for (let j = 0; j < cols.length; j++) {
            // By default driver returns row as a map:
            //   column name -> column value
            collectedRow[colNames[j]] = getCqlObject(cols[j]);
        }
        rows.push(collectedRow);
    }

    return rows;
}

/**
 *
 * @param {rust.QueryResultWrapper} result
 * @returns {Array.<{name, type}>}
 */
function getColumnsMetadata(result) {
    let res = [];
    let columnsWrapper = result.getColumnsSpecs();
    for (let i = 0; i < columnsWrapper.length; i++) {
        let e = columnsWrapper[i];
        res.push({
            ksname: e.ksname,
            tablename: e.tablename,
            name: e.name,
            // TODO: setup correctly type and info
            type: { code: e.typeCode.valueOf(), type: null, info: null },
        });
    }
    return res;
}

module.exports.getCqlObject = getCqlObject;
module.exports.getRowsFromResultsWrapper = getRowsFromResultsWrapper;
module.exports.getColumnsMetadata = getColumnsMetadata;