Source: functions/ensureRepeatingCharacters.js

/**
* simplePass - A JavaScript password generator.
* Copyright (C) 2023  Jordan Vezina(staticBanter)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
'use strict';
import shuffle from "./shuffle.js";
/**
 * @file
 * @module ensureRepeatingCharacters
 */
/**
 * Ensures a string contains repeating characters.
 *
 * @function ensureRepeatingCharacters
 * @param {string} string The string to ensure contains repeating characters.
 * @param {ensureRepeatingCharacterOptions} options An object defining how to repeat the characters in the string.
 * If none of the matching attributes are passed with this object, the string will be returned unmodified.
 * @requires shuffle
 * @returns {string} A string with the desired repeating character requirements.
 */
export default function ensureRepeatingCharacters(string, options) {
    /**
     * Removes repeats of a character found in a string.
     *
     * @function removeRepeats
     * @private
     * @param {Array<any>} string The string to check for repeats.
     * @returns {string} A string containing the original characters not repeated.
     */
    function removeRepeats(string) {
        // The list to contain the unique characters.
        const uniqueSet = [];
        /**
         * Foreach character in the string,
         * if the unique set does not already include that character,
         * add it.
         */
        string.split('').forEach((char) => {
            if (!uniqueSet.includes(char)) {
                uniqueSet.push(char);
            }
        });
        // Return our unique set as a string.
        return uniqueSet.join('');
    }
    /**
     * Split the string into pieces.
     * This makes it easier to ensure the beginning and end characters can be preserved if needed.
     */
    let stringBeginning = string.slice(0, 1);
    let stringMiddle = string.slice(1, string.length - 1);
    let stringEnd = string.slice(string.length - 1, string.length);
    /**
     * Determine if we are:
     *  Custom Repeating Characters
     *  Repeating random characters in the already generated password.
     *  Repeating a single character in the password.
     * * Note: It needs to be checked in this order.
     */
    if (options.customCharacterSet) {
        // ^ Custom Repeating Characters.
        // Initialize our characters list.
        const characters = [];
        // Push all the middle string characters into our list.
        stringMiddle.split('').forEach((char) => {
            characters.push(char);
        });
        /**
         * Check if the attribute is a string else it will be list of sets.
         */
        if (typeof (options.customCharacterSet) === 'string') {
            // Split the repeating characters string up and add each character to our list twice.
            options.customCharacterSet.split('').forEach((char) => {
                characters.push(char);
                characters.push(char);
            });
        }
        else {
            // ^ Custom Repeating Characters should be a list.
            /**
             * Iterate over the list.
             * Foreach set add it's character teh request amount of times.
             */
            options.customCharacterSet.forEach((set) => {
                let i = parseInt(set[1]);
                while (i--) {
                    characters.push(set[0]);
                }
            });
        }
        /**
         * If the beginning or end preservations are not set,
         * push their characters into our list and blank them.
         */
        if (!options.preservations?.beginning) {
            characters.push(stringBeginning);
            stringBeginning = '';
        }
        if (!options.preservations?.end) {
            characters.push(stringEnd);
            stringEnd = '';
        }
        /***/
        // Create our string.
        string = stringBeginning + shuffle(characters).join('') + stringEnd;
    }
    else if (options.repeatingSetCount
        && options.repeatingSetCount > 1) {
        // ^ Repeating random characters in the already generated password.
        // Break up the characters into an array
        let characters = shuffle(string.split(''));
        /**
         * Remove any currently repeating characters,
         * as they will through us off count.
         */
        characters = removeRepeats(characters.join('')).split('');
        stringMiddle = removeRepeats(stringMiddle);
        /***/
        /**
         * Add characters from our list to the middle of the string
         * as many times as we need to repeat character and while a
         * character exists in the list at that position.
         */
        for (let i = 0; (i < options.repeatingSetCount
            && characters[i]); i++) {
            stringMiddle += characters[i];
        }
        // Create our string.
        string = stringBeginning + shuffle(stringMiddle.split('')).join('') + stringEnd;
    }
    else {
        // ^ Repeating a single character in the password.
        // Shuffle the string and add a the first character to the middle.
        stringMiddle += shuffle(string.split(''))[0];
        // Create our string.
        string = stringBeginning + shuffle(stringMiddle.split('')).join('') + stringEnd;
    }
    // Return our string.
    return string;
}