"use strict";
/*------------------------------------------------------*
 * Copyright (c) 2023 Dawid Papiewski "SpacingBat3".    *
 *                                                      *
 * All rights reserved. Licensed under the ISC license. *
 *------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.sanitizeLiteral = exports.parseableRange = void 0;
/**
 * Sets of chars that are correctly understood by current engine and can be
 * transformed to valid {@linkcode charset}.
 *
 * @since v1.0.0
 */
exports.parseableRange = Object.freeze(["a-z", "A-Z", "0-9", "---"]);
/**
 * A type-safe string sanitizer supporting any set of chars while being capable
 * of calculating the expected result as a static type if literal is provided as
 * a name. Predicts the accurate output based on input types or errors (`never`)
 * if function is guaranteed to fail in the runtime.
 *
 * @remarks
 *
 * It is designed to be rather performant at runtime (it uses [`RegExp`]
 * under-the-hood), however be aware of long compilation times as TypeScript
 * will have to do heavy calculations for function result in case of complex
 * strings and character sets (a lot of operations are done in char-by-char
 * manner, using `infer` and type recursion — there's a real chance that for
 * very long literal strings TypeScript will just give up at calculations and
 * end compilation with an error!).
 *
 * [`RegExp`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp – JavaScript | MDN"
 *
 * @privateRemarks
 *
 * This function began, for me and anyone digging into the source code, as a
 * resource for learning advanced TypeScript manipulations on string literals.
 * I will also use it for my own personal projects.
 *
 * @param value - Value to sanitize. Should be a *non-nullish* `string`.
 * @param charset - A string that represents a set of characters. For ranges, only values from {@linkcode parseableRange} are valid.
 * @param replacement - A `char` (i.e. `string` with `length === 0`) which should replace invalid characters inside the string.
 * @param trimMode – Definies how string should be trimmed. Defaults to `left` (compatibility reasons).
 *
 * @returns - Original {@link value} for nullish values, sanitized string for anything else.
 * @throws  - [`TypeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError "TypeError – JavaScript | MDN") for unresolveable {@link charset} or invalid {@link trimMode}, [`RangeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError "RangeError – JavaScript | MDN") for non-char values in {@link replacement} and [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error "Error – JavaScript | MDN") for {@link value} which cannot be sanitized to the expected {@link charset}.
 *
 * @example
 *
 * // (const) regular: "fooBar3"
 * const regular = "fooBar3" as const;
 * // (const) mod1: "FOOBAR3"
 * const mod1 = sanitizeLiteral(regular,"A-Z0-9");
 * // (const) mod2: "foobarz"
 * const mod2 = sanitizeLiteral(regular,"a-z","z");
 * // (const) mod3: "oo_ar3"
 * const mod3 = sanitizeLiteral(regular,"acdeghijklmnopqrstuvwxyz0-9","_");
 *
 * @since v1.0.0
 */
function sanitizeLiteral(value, charset = "a-z0-9", replacement = "-", trimMode = "left") {
    if (value === null || value === undefined)
        return value;
    if ((charset.match(/([^])-([^])/gm) ?? []).find(element => !["a-z", "A-Z", "0-9", "---"].includes(element)) !== undefined)
        throw new TypeError(`Unrecognized charset: "${charset}"!`);
    if (replacement.length !== 1)
        throw new RangeError("Parameter 'replacement' should be a valid character");
    charset = charset.replaceAll(/([\]^\\])/g, "\\$1");
    let valueString;
    const regexp = {
        valid: new RegExp(`[${charset}]`),
        invalid: new RegExp(`[^${charset}${replacement.replaceAll("]", "\\]")}]`, "g")
    };
    if (typeof value !== "string")
        valueString = String(value);
    else
        valueString = value;
    if (regexp.invalid.test(valueString) || valueString.startsWith(replacement)) {
        // Try to convert string to uppercase or lowercase based on charset.
        valueString = !/[A-Z]/.test(charset) ? valueString.toLowerCase() :
            !/[a-z]/.test(charset) ? valueString.toUpperCase() : valueString;
        // Trim string based on the trimMode
        switch (trimMode) {
            case "both":
            //@ts-expect-error – fallthrough intended
            case "left":
                valueString = valueString.slice(valueString.search(regexp.valid));
                if (trimMode === "left")
                    break;
            //@ts-expect-error – fallthrough intended
            case "right":
                valueString = valueString.slice(0, valueString.length - Array.from(valueString).reverse().findIndex(c => regexp.valid.test(c)));
            case null:
                break;
            default:
                throw new TypeError(`Invalid trim mode: "${trimMode}"`);
        }
        // Replace the rest with the replacement character.
        valueString = valueString.replaceAll(regexp.invalid, replacement);
    }
    // Do not accept the empty strings 
    if (valueString.length === 0)
        throw new Error("Parameter 'name' is not sanitizable!");
    return valueString;
}
exports.sanitizeLiteral = sanitizeLiteral;
exports.default = sanitizeLiteral;
//# sourceMappingURL=lib.js.map