@haystacks/async
Version:
A framework to build any number or any kind of native application or automation solution.
458 lines (441 loc) • 22.2 kB
JavaScript
/* eslint-disable no-unused-vars */
/**
* @file stringParsingUtilities.js
* @module stringParsingUtilities
* @description Contains all system defined business rules for parsing strings,
* with values of all kinds, and various parsing operations.
* Excluding functions that use the loggers.
* @requires {@link https://www.npmjs.com/package/@haystacks/constants|@haystacks/constants}
* @requires {@link https://www.npmjs.com/package/path|path}
* @author Seth Hollingsead
* @date 2021/12/28
* @copyright Copyright © 2022-… by Seth Hollingsead. All rights reserved
*/
// Internal imports
// External imports
import hayConst from '@haystacks/constants';
import path from 'path';
const {bas, gen, sys, wrd} = hayConst;
const baseFileName = path.basename(import.meta.url, path.extname(import.meta.url));
// framework.businessRules.rules.stringParsingUtilities.
const namespacePrefix = wrd.cframework + bas.cDot + sys.cbusinessRules + bas.cDot + wrd.crules + bas.cDot + baseFileName + bas.cDot;
/**
* @function parseSystemRootPath
* @description Parses the root path as returned by calling: path.resolve(__dirname);
* This business rule looks for the "AppName" part of the path
* and will parse that out to determine where on the hard drive this "appName" folder is installed at.
* @NOTE: The "AppName" is derived from the configuration settings called "applicationName"
* which should have been set by the system when it was loaded.
* @param {string} inputData The root path as defined by calling path.resolve(__dirname);
* @param {string} inputMetaData The name of the application.
* @return {string} A string with the path up to the application folder,
* where ever that is installed on the local system currently executing.
* @author Seth Hollingsead
* @date 2021/10/27
* @NOTE Cannot use the loggers here, because dependency data will have never been loaded.
*/
async function parseSystemRootPath(inputData, inputMetaData) {
// let functionName = parseSystemRootPath.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`inputData is: ${JSON.stringify(inputData)}`);
// console.log(`inputMetaData is: ${JSON.stringify(inputMetaData)}`);
let returnData = '';
if (inputData && inputMetaData) {
let applicationName;
let pathElements = '';
applicationName = await parseSystemRootPathApplicationName(inputMetaData);
if (inputData.includes(bas.cBackSlash) === true) {
// console.log('caught the case of back slash');
pathElements = inputData.split(bas.cBackSlash);
} else if (inputData.includes(bas.cForwardSlash) === true) {
// console.log('caught the case of forward slash');
pathElements = inputData.split(bas.cForwardSlash);
}
for (let i = 0; i < pathElements.length; i++) {
// console.log(`BEGIN iteration i: ${i}`);
let pathElement = pathElements[i];
// console.log(`pathElement is: ${pathElement}`);
if (i === 0) {
// console.log('case: i === 0');
returnData = pathElement;
} else if (pathElement === applicationName || pathElement === inputMetaData || pathElement.includes(applicationName)) {
// console.log(`case: pathElement === ${applicationName}`);
returnData = returnData + bas.cBackSlash + pathElement + bas.cBackSlash; // `${returnData}\\${pathElement}\\`;
break;
} else {
// console.log('case else');
returnData = returnData + bas.cBackSlash + pathElement; // `${returnData}\\${pathElement}`;
}
// console.log('returnData is: ' + returnData);
} // End for-loop: (let i = 0; i < pathElements.length; i++)
} // End-if (inputData)
// console.log(`returnData is: ${JSON.stringify(returnData)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return returnData;
}
/**
* @function parseSystemRootPathApplicationName
* @description Does the work of finding the application name or a component of the application name for hyphenated application names.
* @param {string} inputData The name of the application that should be processed.
* @param {string} inputMetaData Not used for this business rule.
* @return {string} The name of the application or a component of the application name.
* @author Seth Hollingsead
* @date 2023/03/15
*/
async function parseSystemRootPathApplicationName(inputData, inputMetaData) {
// let functionName = parseSystemRootPathApplicationName.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`inputData is: ${JSON.stringify(inputData)}`);
// console.log(`inputMetaData is: ${JSON.stringify(inputMetaData)}`);
let returnData = '';
// Check for either "/" or "-", catch the case for application name: @haystacks/async && @haystacks-async
if (inputData && (inputData.includes(bas.cForwardSlash) || inputData.includes(bas.cDash))) {
let applicationNameArray = [];
if (inputData.includes(bas.cForwardSlash)) {
applicationNameArray = inputData.split(bas.cForwardSlash);
} else if (inputData.includes(bas.cDash)) {
applicationNameArray = inputData.split(bas.cDash);
}
// console.log('applicationNameArray is: ' + JSON.stringify(applicationNameArray));
// NOTE: Cannot have an else statement above because we don't know how the user will name their application.
// Assign the application name to the final name-element of the repo-namespace.
returnData = applicationNameArray[applicationNameArray.length - 1];
// The above code handles the case that the framework is: @haystacks/sync or @haystacks/async
// Then the path will pickup to the "sync" or "async" and everything up to the point as part of the return path.
// console.log('capturing part of the application name: ' + applicationName);
} else {
returnData = inputData; // Rename it for readability.
// console.log('applicationName is: ' + applicationName);
}
// console.log(`returnData is: ${JSON.stringify(returnData)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return returnData;
}
/**
* @function stringToDataType
* @description Converts a string to the appropriate data value.
* So if it's a string value of "3.1415926535897932384626433832" Then it will get converted to a float of the same value.
* If it's a string value of "false" then it will get converted to a boolean of the same value.
* If it's a string value of "12" then it will get converted to an integer of the same value.
* If it's a string value of "Happy Birthday" it will get returned the same as the input, no change, since it's just a string.
* If it's an array of strings, or collection object, it will get returned as the same as the input, no change.
* @param {string} inputData The string that should be converted to some value.
* @param {string} inputMetaData Not used for this business rule.
* @return {object|string|boolean|integer} Returns a value of whatever type the string should be converted to as appropriate.
* @author Seth Hollingsead
* @date 2021/11/10
* @NOTE Cannot use the loggers here, because of a circular dependency.
*/
async function stringToDataType(inputData, inputMetaData) {
// let functionName = stringToDataType.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`inputData is: ${JSON.stringify(inputData)}`);
// console.log(`inputMetaData is: ${JSON.stringify(inputMetaData)}`);
let returnData = false;
if (inputData !== undefined) {
let dataType = await determineObjectDataType(inputData, '');
switch (dataType) {
case wrd.cBoolean:
returnData = await stringToBoolean(inputData, '');
break;
case wrd.cInteger:
returnData = parseInt(inputData, 10);
break;
case wrd.cFloat:
returnData = parseFloat(inputData, '');
break;
case wrd.cString:
returnData = inputData;
break;
default: // We don't know what kind of object this is, better just return it the way it is.
returnData = inputData;
break;
} // End-switch (dataType)
} // End-if (inputData)
// console.log(`returnData is: ${JSON.stringify(returnData)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return returnData;
}
/**
* @function stringToBoolean
* @description Converts a string to a boolean value.
* @param {string} inputData A string that contains a truthy or falsy value and should be converted to a boolean value.
* @param {string} inputMetaData Not used for this business rule.
* @return {boolean} A Boolean value of either True or False according to the business rule conversion.
* @author Seth Hollingsead
* @date 2021/11/10
* @NOTE We cannot pass in a 1 or 0 to this function and expect it to evaluate as a True or False because:
* We have another function that is passing strings into the function, and also part of that check to look for data-types is a check to see if a string is a number.
* If we cause this function to evaluate a 0 or 1 to a Boolean, then the integer function would never get a chance to evaluate.
* @NOTE Cannot use the loggers here, because of a circular dependency.
*/
async function stringToBoolean(inputData, inputMetaData) {
// let functionName = stringToBoolean.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`inputData is: ${JSON.stringify(inputData)}`);
// console.log(`inputMetaData is: ${JSON.stringify(inputMetaData)}`);
let returnData = false;
if (inputData) {
if (typeof inputData === wrd.cboolean) {
returnData = inputData;
} else {
switch (inputData.toLowerCase().trim()) {
case gen.ctrue: case bas.ct: case bas.cy: case gen.cyes: case bas.con:
returnData = true;
break;
case gen.cfalse: case bas.cf: case bas.cn: case bas.cno: case gen.coff:
returnData = false;
break;
default:
returnData = false;
break;
} // End-switch (inputData.toLowerCase().trim())
}
} // End-if (inputData)
// console.log(`returnData is: ${JSON.stringify(returnData)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return returnData;
}
/**
* @function determineObjectDataType
* @description Determines if the contents of a string are actually a Boolean, Integer, Float, String or something else.
* @param {string} inputData A string that contains some value that we should figure out
* what kind of data type that data is, Boolean, Integer, Float, String or something else.
* @param {string} inputMetaData Not used for this business rule.
* @return {string} A string that indicates if the data type should be Boolean, Integer, Float, String or something else.
* @author Seth Hollingsead
* @date 2021/11/10
* @NOTE Cannot use the loggers here, because of a circular dependency.
*/
async function determineObjectDataType(inputData, inputMetaData) {
// let functionName = determineObjectDataType.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`inputData is: ${JSON.stringify(inputData)}`);
// console.log(`inputMetaData is: ${JSON.stringify(inputMetaData)}`);
let returnData = false;
if (inputData) {
if (await isBoolean(inputData, '') === true) {
returnData = wrd.cBoolean;
} else if (await isInteger(inputData, '') === true) {
returnData = wrd.cInteger;
} else if (await isFloat(inputData, '') === true) {
returnData = wrd.cFloat;
} else if (await isString(inputData, '') === true) {
returnData = wrd.cString;
} else { // Otherwise we cannot figure out what the data type is.
// No real way to tell the difference between Short, Long and Double.
// And we don't really need to tell the difference between all these complicated data types.
// At least not yet!
returnData = wrd.cObject;
}
} // End-if (inputData)
// console.log(`returnData is: ${JSON.stringify(returnData)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return returnData;
}
/**
* @function isBoolean
* @description Determines if the input string is a boolean type of value,
* "true", "True", "TRUE", "t", "T", "y", "Y", "yes", "Yes", "YES", "on", "On", "ON" or
* "false", "False", "FALSE", "f", "F", "n", "N", "no", "No", "NO"
* @param {string} inputData The string that should be checked if it is a Boolean style value or not, could be some form of "true" or "false".
* @param {string} inputMetaData Not used for this business rule.
* @return {boolean} A Boolean value of True or False to indicate if the input string is a Boolean or not.
* @author Seth Hollingsead
* @date 2021/11/10
* @NOTE Cannot use the loggers here, because of a circular dependency.
*/
async function isBoolean(inputData, inputMetaData) {
// let functionName = isBoolean.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`inputData is: ${JSON.stringify(inputData)}`);
// console.log(`inputMetaData is: ${JSON.stringify(inputMetaData)}`);
let returnData = false;
if (inputData) {
if (typeof inputData === 'boolean') {
returnData = true;
} else if (typeof inputData === 'number') {
returnData = false;
} else {
inputData = inputData.toLowerCase().trim();
if (inputData === gen.ctrue || inputData === bas.ct || inputData === bas.cy || inputData === gen.cyes || inputData === bas.con ||
inputData === gen.cfalse || inputData === bas.cf || inputData === bas.cn || inputData === bas.cno || inputData === gen.coff) {
returnData = true;
} else {
returnData = false;
}
}
} // End-if (inputData)
// console.log(`returnData is: ${JSON.stringify(returnData)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return returnData;
}
/**
* @function isInteger
* @description Determines if the input string is an integer type of value -9007299254740992 to 9007299254740992.
* @param {string} inputData The string that should be checked if it is an integer style value or not.
* @param {string} inputMetaData Not used for this business rule.
* @return {boolean} A Boolean value of true or false to indicate if the input string is an integer or not.
* @author Seth Hollingsead
* @date 2021/11/10
* @NOTE Cannot use the loggers here, because of a circular dependency.
*/
async function isInteger(inputData, inputMetaData) {
// let functionName = isInteger.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`inputData is: ${JSON.stringify(inputData)}`);
// console.log(`inputMetaData is: ${JSON.stringify(inputMetaData)}`);
let returnData = false;
if (inputData !== undefined) {
if (!isNaN(inputData)) {
if (inputData % 1 === 0) {
// It's a whole number, aka: integer
returnData = true;
} else { // Else clause is redundant, but kept here for code completeness.
// Might be a number, but not a whole number.
returnData = false;
}
} else { // Else clause is redundant, but kept here for code completeness.
// Possibly also console log here for debugging.
returnData = false;
}
} // End-if (inputData)
// console.log(`returnData is: ${JSON.stringify(returnData)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return returnData;
}
/**
* @function isFloat
* @description Determines if the input string is a floating point type of value or not.
* @param {string} inputData The string that should be checked if it is an integer style value or not.
* @param {string} inputMetaData Not used for this business rule.
* @return {boolean} A Boolean value of true or false to indicate if the input string is a floating point number or not.
* @author Seth Hollingsead
* @date 2021/11/10
* @NOTE Cannot use the loggers here, because of a circular dependency.
*/
async function isFloat(inputData, inputMetaData) {
// let functionName = isFloat.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`inputData is: ${JSON.stringify(inputData)}`);
// console.log(`inputMetaData is: ${JSON.stringify(inputMetaData)}`);
let returnData = false;
if (inputData) {
if (!isNaN(inputData) && inputData.indexOf(bas.cDot) !== -1) {
returnData = true;
} else { // Else clause is redundant, but kept here for code completeness.
// Possibly also console log here for debugging.
returnData = false;
}
} // End-if (inputData)
// console.log(`returnData is: ${JSON.stringify(returnData)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return returnData;
}
/**
* @function isString
* @description Determines if the input string is a string or not, by process of elimination,
* aka if it's not a Boolean, and not an Integer and not a Float then it must be a string.
* @param {string} inputData The string that should be checked if it is a string and not a Boolean, Integer or Float.
* @param {string} inputMetaData Not used for this business rule.
* @return {boolean} A Boolean value of true or false to indicate if the input string is a string and
* not a Boolean, Integer or Float; or not (meaning it would be one of those 3 data types, disguised as a string).
* @author Seth Hollingsead
* @date 2021/11/10
* @NOTE Cannot use the loggers here, because of a circular dependency.
*/
async function isString(inputData, inputMetaData) {
// let functionName = isString.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`inputData is: ${JSON.stringify(inputData)}`);
// console.log(`inputMetaData is: ${JSON.stringify(inputMetaData)}`);
let returnData = false;
if (inputData) {
if (await isBoolean(inputData, '') === false && await isInteger(inputData, '') === false && await isFloat(inputData, '') === false &&
(typeof inputData === wrd.cstring || inputData instanceof String)) {
returnData = true; // If it's not a Boolean, and not an Integer, and not a Float, then it must be a string,
// especially given the type of the variable is a string!
} else { // Else clause is redundant, but kept here for code completeness.
// Possibly also console log here for debugging.
returnData = false;
}
} // End-if (inputData)
// console.log(`returnData is: ${JSON.stringify(returnData)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return returnData;
}
/**
* @function replaceDoublePercentWithMessage
* @description Parses the input string and replaces any instance of a double percentage sign
* with the input Meta Data string.
* @param {string} inputData The string that might contain the double percentage signs.
* @param {string} inputMetaData The string that should replace the double percentage signs.
* @return {string} The modified string with the message inserted.
* @author Seth Hollingsead
* @date 2021/12/24
* @NOTE Cannot use the loggers here, because of a circular dependency.
*/
async function replaceDoublePercentWithMessage(inputData, inputMetaData) {
// let functionName = replaceDoublePercentWithMessage.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`inputData is: ${JSON.stringify(inputData)}`);
// console.log(`inputMetaData is: ${JSON.stringify(inputMetaData)}`);
let returnData = '';
if (inputData) {
returnData = await utilitiesReplaceCharacterWithCharacter(inputData, [bas.cDoublePercent, inputMetaData]);
}
// console.log(`returnData is: ${JSON.stringify(returnData)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return returnData;
}
/**
* @function utilitiesReplaceCharacterWithCharacter
* @description Replaces all of the specified character in the inputData with another specified character.
* @param {string} inputData A string that may or may not contain the specified
* characters that should be converted to another specified character.
* @param {array<string,string>} inputMetaData An array of data that contains 2 additional string parameters:
* inputMetaData[0] === character2Find - The character to be searched and replaced from the input string.
* inputMetaData[1] === character2Replace - The character that should be used to replace
* the character specified for replacement from the input data.
* @return {string} The same as the input string but with specified characters converted to the other specified character.
* @author Seth Hollingsead
* @date 2021/12/28
*/
async function utilitiesReplaceCharacterWithCharacter(inputData, inputMetaData) {
// let functionName = utilitiesReplaceCharacterWithCharacter.name;
// console.log(`BEGIN ${namespacePrefix}${functionName} function`);
// console.log(`inputData is: ${JSON.stringify(inputData)}`);
// console.log(`inputMetaData is: ${JSON.stringify(inputMetaData)}`);
let returnData;
let character2Find = inputMetaData[0];
let character2Replace = inputMetaData[1];
if (!inputData && !character2Find && !character2Replace) {
// console.log('Either inputData, character2Find or character2Replace are undefined');
// console.log(`character2Find is: ${JSON.stringify(character2Find)}`);
// console.log(`character2Replace is: ${JSON.stringify(character2Replace)}`);
returnData = false;
} else {
// console.log(`character2Find is: ${JSON.stringify(character2Find)}`);
// console.log(`character2Replace is: ${JSON.stringify(character2Replace)}`);
if (Array.isArray(inputData) === true) {
inputData = inputData[0];
}
returnData = inputData.replaceAll(character2Find, character2Replace);
}
// console.log(`returnData is: ${JSON.stringify(returnData)}`);
// console.log(`END ${namespacePrefix}${functionName} function`);
return returnData;
}
export default {
parseSystemRootPath,
parseSystemRootPathApplicationName,
stringToDataType,
stringToBoolean,
determineObjectDataType,
isBoolean,
isInteger,
isFloat,
isString,
replaceDoublePercentWithMessage,
utilitiesReplaceCharacterWithCharacter
};