mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-12-28 17:44:54 -05:00
Implemented mapEnumInput
, so I can extract enums from options
This commit is contained in:
parent
44e418ffe7
commit
36b3d69f46
2 changed files with 119 additions and 8 deletions
|
@ -1,5 +1,9 @@
|
|||
import process from "process";
|
||||
|
||||
interface EnumLike<T = number> {
|
||||
[i: string | number | symbol]: T | string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface InputObject extends Record<string, string | InputObject> { }
|
||||
|
||||
|
@ -34,11 +38,11 @@ function init(root: InputObject, path: string[], value: string): void {
|
|||
}
|
||||
|
||||
export function mapStringInput(value: any, defaultValue = ""): string {
|
||||
return mapInput(value, defaultValue ?? "");
|
||||
return mapInput(value, defaultValue ?? "", null, "string");
|
||||
}
|
||||
|
||||
export function mapObjectInput(value: any, defaultValue: object = null): object {
|
||||
return mapInput(value, defaultValue ?? null);
|
||||
return mapInput(value, defaultValue ?? null, null, "object");
|
||||
}
|
||||
|
||||
export function mapNumberInput(value: any, defaultValue = 0): number {
|
||||
|
@ -47,7 +51,7 @@ export function mapNumberInput(value: any, defaultValue = 0): number {
|
|||
const num = +x;
|
||||
return isNaN(num) ? undefined : num;
|
||||
}
|
||||
});
|
||||
}, "number");
|
||||
}
|
||||
|
||||
export function mapBooleanInput(value: any, defaultValue = false): boolean {
|
||||
|
@ -60,22 +64,73 @@ export function mapBooleanInput(value: any, defaultValue = false): boolean {
|
|||
undefined
|
||||
);
|
||||
}
|
||||
});
|
||||
}, "boolean");
|
||||
}
|
||||
|
||||
export function mapInput<T>(value: any, fallbackValue: T, mappers?: Record<string, (x: any) => T | undefined>): T {
|
||||
function findEnumValueByName<T extends EnumLike<U>, U>(enumClass: T, name: string): U | undefined {
|
||||
if (typeof enumClass[+name] === "string") {
|
||||
return <U><unknown>+name;
|
||||
}
|
||||
|
||||
if (enumClass[name] !== undefined) {
|
||||
return <U>enumClass[name];
|
||||
}
|
||||
|
||||
const entries = Object.entries(enumClass);
|
||||
for (const [key, value] of entries) {
|
||||
if (key.localeCompare(name, undefined, { sensitivity: "base" }) === 0) {
|
||||
return <U>value;
|
||||
}
|
||||
}
|
||||
for (const [key, value] of entries) {
|
||||
if (key.trim().replace(/[-_]/g, "").localeCompare(name.trim().replace(/[-_]/g, ""), undefined, { sensitivity: "base" }) === 0) {
|
||||
return <U>value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function mapEnumInput<T extends EnumLike<U>, U>(value: any, enumClass: T, defaultValue: U = null): U | null {
|
||||
return mapInput(value, defaultValue, {
|
||||
string: (x: string) => {
|
||||
let result: U = undefined;
|
||||
|
||||
let i = 0;
|
||||
while (i < x.length) {
|
||||
let separatorIndex = x.indexOf("|", i);
|
||||
if (separatorIndex === -1) {
|
||||
separatorIndex = x.length;
|
||||
}
|
||||
|
||||
const currentValue = findEnumValueByName<T, U>(enumClass, x.substring(i, separatorIndex));
|
||||
if (result === undefined || currentValue !== undefined && typeof currentValue !== "number") {
|
||||
result = currentValue;
|
||||
} else {
|
||||
result = <U><unknown>(<number><unknown>result | <number><unknown>currentValue);
|
||||
}
|
||||
|
||||
i = separatorIndex + 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}, "number");
|
||||
}
|
||||
|
||||
export function mapInput<T>(value: any, fallbackValue?: T, mappers?: Record<string, (x: any) => T | undefined>, valueType?: string): T {
|
||||
if (value === undefinedValue || value === undefined || value === null) {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
if (typeof value === typeof fallbackValue) {
|
||||
valueType ??= typeof fallbackValue;
|
||||
if (typeof value === valueType) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const mapper = mappers?.[typeof value];
|
||||
if (mapper) {
|
||||
const mappedValue = mapper(value);
|
||||
if (typeof mappedValue === typeof fallbackValue) {
|
||||
if (typeof mappedValue === valueType) {
|
||||
return mappedValue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, test, expect, beforeAll, afterAll } from "@jest/globals";
|
||||
import { setupInput, unsetInput } from "./utils/input-utils";
|
||||
import { getInputAsObject, mapStringInput, mapObjectInput, mapNumberInput, mapBooleanInput } from "../src/utils/input-utils";
|
||||
import { getInputAsObject, mapStringInput, mapObjectInput, mapNumberInput, mapBooleanInput, mapEnumInput } from "../src/utils/input-utils";
|
||||
|
||||
const defaultInput = {
|
||||
"boolean": true,
|
||||
|
@ -195,3 +195,59 @@ describe("mapBooleanInput", () => {
|
|||
expect(mapBooleanInput(input["booleanfalsestringuppercasewithwhitespace"], true)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("mapEnumInput", () => {
|
||||
enum TestEnum {
|
||||
None = 0,
|
||||
|
||||
A = 1,
|
||||
B = 2,
|
||||
C = 4,
|
||||
|
||||
A_B = 1 | 2,
|
||||
A_C = 1 | 4,
|
||||
B_C = 2 | 4,
|
||||
|
||||
A_B_C = 1 | 2 | 4,
|
||||
}
|
||||
|
||||
beforeAll(() => setupInput({
|
||||
...defaultInput,
|
||||
enumAB: TestEnum.A_B,
|
||||
enumABStringUpperCase: "A_B",
|
||||
enumABStringLowerCase: "a_b",
|
||||
enumABStringLowerCaseWithWhitespace: " a_b ",
|
||||
enumABStringLowerCaseWithWhitespaceAndDifferentSeparator: " a-b ",
|
||||
|
||||
enumABStringLowerCaseWithWhitespaceBitwise: " a | b ",
|
||||
enumABCStringLowerCaseWithWhitespaceBitwise: " c | b | b | a ",
|
||||
}));
|
||||
afterAll(() => unsetInput());
|
||||
|
||||
test("returns default value if input cannot be casted to the given enum", () => {
|
||||
const input = getInputAsObject();
|
||||
|
||||
expect(input["object"]).not.toBeUndefined();
|
||||
expect(mapEnumInput(input["object"], TestEnum)).toBeNull();
|
||||
|
||||
expect(input["boolean"]).not.toBeUndefined();
|
||||
expect(mapEnumInput(input["boolean"], TestEnum)).toBeNull();
|
||||
|
||||
expect(input["array"]).not.toBeUndefined();
|
||||
expect(mapEnumInput(input["array"], TestEnum)).toBeNull();
|
||||
|
||||
expect(input["undefined"]).toBeUndefined();
|
||||
expect(mapEnumInput(input["undefined"], TestEnum, TestEnum.A_B_C)).toBe(TestEnum.A_B_C);
|
||||
});
|
||||
|
||||
test("maps values to the given enum", () => {
|
||||
const input = getInputAsObject();
|
||||
expect(mapEnumInput(input["enumab"], TestEnum)).toBe(TestEnum.A_B);
|
||||
expect(mapEnumInput(input["enumabstringuppercase"], TestEnum)).toBe(TestEnum.A_B);
|
||||
expect(mapEnumInput(input["enumabstringlowercase"], TestEnum)).toBe(TestEnum.A_B);
|
||||
expect(mapEnumInput(input["enumabstringlowercasewithwhitespace"], TestEnum)).toBe(TestEnum.A_B);
|
||||
expect(mapEnumInput(input["enumabstringlowercasewithwhitespaceanddifferentseparator"], TestEnum)).toBe(TestEnum.A_B);
|
||||
expect(mapEnumInput(input["enumabstringlowercasewithwhitespacebitwise"], TestEnum)).toBe(TestEnum.A_B);
|
||||
expect(mapEnumInput(input["enumabcstringlowercasewithwhitespacebitwise"], TestEnum, TestEnum.A_B)).toBe(TestEnum.A_B_C);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue