'use strict';
const Rac = require('../Rac');
/**
* Internal utilities.
*
* Available through `{@link Rac.utils}` or [`rac.utils`]{@link Rac#utils}.
*
* @namespace utils
*/
/**
* Asserts that all passed parameters are objects or primitives. If any
* parameter is `null` or `undefined` a `{@link Rac.Exception.failedAssert}`
* is thrown.
*
* @param {...(Object|primitive)} parameters
* @returns {Boolean}
*
* @function assertExists
* @memberof utils#
*/
exports.assertExists = function(...parameters) {
parameters.forEach((item, index) => {
if (item === null) {
throw Rac.Exception.failedAssert(
`Found null, expecting element to exist at index ${index}`);
}
if (item === undefined) {
throw Rac.Exception.failedAssert(
`Found undefined, expecting element to exist at index ${index}`);
}
});
}
/**
* Asserts that all `elements` are objects or the given `type`, otherwise a
* `{@link Rac.Exception.failedAssert}` is thrown.
*
* When any member of `elements` is `null` or `undefined`, the exception is
* also thrown.
*
* @param {function} type
* @param {...Object} elements
*
* @returns {Boolean}
*
* @function assertType
* @memberof utils#
*/
exports.assertType = function(type, ...elements) {
elements.forEach(item => {
if (!(item instanceof type)) {
throw Rac.Exception.failedAssert(
`Element is unexpected type - element-type:${typeName(item)} expected-type-name:${type.name} element:${item}`);
}
});
}
/**
* Asserts that all `elements` are number primitives and not NaN, otherwise
* a `{@link Rac.Exception.failedAssert}` is thrown.
*
* @param {...Number} elements
* @returns {Boolean}
*
* @function assertNumber
* @memberof utils#
*/
exports.assertNumber = function(...elements) {
elements.forEach(item => {
if (typeof item !== 'number' || isNaN(item)) {
throw Rac.Exception.failedAssert(
`Element is unexpected type, expecting number primitive - element-type:${typeName(item)} element:${item}`);
}
});
}
/**
* Asserts that all `elements` are string primitives, otherwise
* a `{@link Rac.Exception.failedAssert}` is thrown.
*
* @param {...String} elements
* @returns {Boolean}
*
* @function assertString
* @memberof utils#
*/
exports.assertString = function(...elements) {
elements.forEach(item => {
if (typeof item !== 'string') {
throw Rac.Exception.failedAssert(
`Element is unexpected type, expecting string primitive - element-type:${typeName(item)} element:${item}`);
}
});
}
/**
* Asserts that all `elements` are boolean primitives, otherwise a
* `{@link Rac.Exception.failedAssert}` is thrown.
*
* @param {...Boolean} elements
* @returns {Boolean}
*
* @function assertBoolean
* @memberof utils#
*/
exports.assertBoolean = function(...elements) {
elements.forEach(item => {
if (typeof item !== 'boolean') {
throw Rac.Exception.failedAssert(
`Element is unexpected type, expecting boolean primitive - element-type:${typeName(item)} element:${item}`);
}
});
}
/**
* Returns the constructor name of `obj`, or its type name.
* Convenience function for debugging and errors.
*
* @param {Object} obj - An `Object` to get its type name
* @returns {String}
*
* @function typeName
* @memberof utils#
*/
function typeName(obj) {
if (obj === undefined) { return 'undefined'; }
if (obj === null) { return 'null'; }
if (typeof obj === 'function' && obj.name != null) {
return obj.name == ''
? `function`
: `function:${obj.name}`;
}
return obj.constructor.name ?? typeof obj;
}
exports.typeName = typeName;
/**
* Adds a constant to the given object, the constant is not enumerable and
* not configurable.
*
* @function addConstantTo
* @memberof utils#
*/
exports.addConstantTo = function(obj, propName, value) {
Object.defineProperty(obj, propName, {
enumerable: false,
configurable: false,
writable: false,
value: value
});
}
/**
* Returns a string representation of `number` displaying all available
* digits, or formmatted used fixed-point notation limited to `digits`.
*
* @param {Number} number - The number to format
* @param {?Number} [digits] - The amount of digits to print, or `null` to
* print all digits
*
* @returns {String}
*
* @function cutDigits
* @memberof utils#
*/
exports.cutDigits = function(number, digits = null) {
return digits === null
? number.toString()
: number.toFixed(digits);
}
/**
* Returns `true` if text oriented with the given `angleTurn` would be
* printed upright.
*
* Angle turn values in the range _[3/4, 1/4)_ are considered upright.
*
* @param {Number} angleTurn - The turn value of the angle to check
* @returns {Boolean}
*
* @function isUprightText
* @memberof utils#
*/
exports.isUprightText = function(angleTurn) {
return angleTurn >= 3/4 || angleTurn < 1/4;
}