Source: tx.js

'use strict';

let transit = require('transit-js');
let shared = require('./shared.js');

/**
 * A transaction builder.
 *
 * @deprecated Use EDN template strings instead.
 *
 * @constructor TxBuilder
 */
function TxBuilder() {
    this._txes = [];
}

function convertE(e) {
    if (typeof e == 'string') {
        if (/[0-9]+/.exec(e) != null) {
            return transit.bigInt(e);
        } else {
            return e;
        }
    } else if (typeof e == 'number') {
        return Math.floor(e);
    } else if (e instanceof shared.BigInt) {
        return transit.bigInt(e.rep);
    } else if (Array.isArray(e) && e.length === 2) {
        return [transit.keyword(e[0]), convertV(e[1])];
    } else if (transit.isInteger(e)) {
        return e;
    } else {
        throw new Error('failed to convert value to entity: ' + e);
    }
}

function convertV(v) {
    if (v instanceof shared.UUID) {
        return transit.uuid(v.rep);
    } else if (v instanceof shared.BigInt) {
        return transit.bigInt(v.rep);
    } else if (v instanceof shared.BigDec) {
        return transit.bigDec(v.rep);
    } else if (Array.isArray(v)) {
        return v.map(convertV);
    } else if (typeof v == 'object') {
        return convertMap(v);
    } else {
        return v;
    }
}

function convertMap(obj) {
    let result = transit.map();
    for (let k in obj) {
        if (obj.hasOwnProperty(k)) {
            let ks = k.toString();
            if (ks === 'db/id' || ks === ':db/id') {
                result.set(keyword(k), convertE(obj[k]));
            } else if (
                ks === 'db/ident' || ks === ':db/ident' ||
                ks === 'db/valueType' || ks === ':db/valueType' ||
                ks === 'db/cardinality' || ks === ':db/cardinality' ||
                ks === 'db/unique' || ks === ':db/unique'
            ) {
                result.set(keyword(k), keyword(obj[k]));
            } else {
                result.set(keyword(k), convertV(obj[k]));
            }
        }
    }
    return result;
}

/**
 * Append map transaction data to this transaction.
 *
 * @param objs Objects that contain the transaction data.
 * @returns {TxBuilder} This instance.
 */
TxBuilder.prototype.txData = function(...objs) {
    objs.forEach((o) => this._txes.push(convertMap(o)));
    return this;
};

/**
 * Add a fact to the transaction.
 *
 * @param e {String|Number|shared.BigInt|Array} The entity.
 * @param a {String} The attribute.
 * @param v {*} The value.
 * @returns {TxBuilder} This instance.
 */
TxBuilder.prototype.add = function(e, a, v) {
    this._txes.push([transit.keyword('db/add'), convertE(e), keyword(a), convertV(v)]);
    return this;
};

/**
 * Retract a fact from the transaction.
 *
 * @param e {String|Number|shared.BigInt|Array} The entity.
 * @param a {String} The attribute.
 * @param v {*} The value.
 * @returns {TxBuilder} This instance.
 */
TxBuilder.prototype.retract = function(e, a, v) {
    this._txes.push([transit.keyword('db/retract'), convertE(e), keyword(a), convertV(v)]);
    return this;
};

/**
 * Retract an entity.
 *
 * @param e {String|Number|shared.BigInt|Array} The entity.
 * @returns {TxBuilder} This instance.
 */
TxBuilder.prototype.retractEntity = function(e) {
    this._txes.push([transit.keyword('db/retractEntity'), convertE(e)]);
    return this;
};

/**
 * Add a compare-and-set operation.
 *
 * @param e {String|Number|shared.BigInt|Array} The entity.
 * @param a {String} The attribute.
 * @param expVal {*} The expected value (to swap out).
 * @param newVal {*} The new value (to swap in).
 * @returns {TxBuilder} This instance.
 */
TxBuilder.prototype.cas = function(e, a, expVal, newVal) {
    this._txes.push([transit.keyword('db/cas'), convertE(e), keyword(a), convertV(expVal), convertV(newVal)]);
    return this;
};

/**
 * Add an arbitrary transaction function call.
 *
 * Note that if you intend to pass symbols or keywords to your transaction
 * function, you must convert those arguments to that type before you pass
 * those arguments to this function.
 *
 * @param functionName {String} The function name.
 * @param args {*} The arguments.
 * @returns {TxBuilder} This instance.
 */
TxBuilder.prototype.txFunction = function(functionName, ...args) {
    let list = [transit.symbol(functionName)];
    this._txes.push(list.concat(args));
    return this;
};

/**
 * Build the transaction.
 *
 * @returns {Array} The transaction data.
 */
TxBuilder.prototype.build = function() {
    return this._txes;
};

/**
 * Convert the argument to a keyword.
 *
 * @param s The string or keyword.
 * @returns {*} The keyword.
 */
function keyword(s) {
    if (transit.isKeyword(s)) {
        return s;
    } else if (s[0] === ':') {
        return transit.keyword(s.slice(1));
    } else {
        return transit.keyword(s);
    }
}

/**
 * Convert the argument to a symbol.
 *
 * @param s A string, or a symbol.
 * @returns {*} The symbol.
 */
function symbol(s) {
    if (transit.isSymbol(s)) {
        return s;
    } else {
        return transit.symbol(s);
    }
}

/**
 * Convert the argument string to a {@link shared.UUID}.
 *
 * @param s The string.
 * @returns {UUID} The UUID.
 */
function uuid(s) {
    if (s instanceof shared.UUID) {
        return s;
    } else {
        return new shared.UUID(s);
    }
}

/**
 * Convert the argument to a {@link shared.BigInt}.
 *
 * @param s The input.
 * @returns {shared.BigInt} The big int.
 */
function bigInt(s) {
    if (s instanceof shared.BigInt) {
        return s;
    } else {
        return new shared.BigInt(s);
    }
}

/**
 * Convert the argument to a {@link shared.BigDec}.
 *
 * @param s The input.
 * @returns {BigDec} The big decimal.
 */
function bigDec(s) {
    if (s instanceof shared.BigDec) {
        return s;
    } else {
        return new shared.BigDec(s);
    }
}

exports.TxBuilder = TxBuilder;
exports.keyword = keyword;
exports.symbol = symbol;
exports.uuid = uuid;
exports.bigInt = bigInt;
exports.bigDec = bigDec;
exports.convertE = convertE;
exports.convertV = convertV;