mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-11-28 19:31:03 -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";
|
import process from "process";
|
||||||
|
|
||||||
|
interface EnumLike<T = number> {
|
||||||
|
[i: string | number | symbol]: T | string;
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
interface InputObject extends Record<string, string | InputObject> { }
|
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 {
|
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 {
|
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 {
|
export function mapNumberInput(value: any, defaultValue = 0): number {
|
||||||
|
@ -47,7 +51,7 @@ export function mapNumberInput(value: any, defaultValue = 0): number {
|
||||||
const num = +x;
|
const num = +x;
|
||||||
return isNaN(num) ? undefined : num;
|
return isNaN(num) ? undefined : num;
|
||||||
}
|
}
|
||||||
});
|
}, "number");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapBooleanInput(value: any, defaultValue = false): boolean {
|
export function mapBooleanInput(value: any, defaultValue = false): boolean {
|
||||||
|
@ -60,22 +64,73 @@ export function mapBooleanInput(value: any, defaultValue = false): boolean {
|
||||||
undefined
|
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) {
|
if (value === undefinedValue || value === undefined || value === null) {
|
||||||
return fallbackValue;
|
return fallbackValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof value === typeof fallbackValue) {
|
valueType ??= typeof fallbackValue;
|
||||||
|
if (typeof value === valueType) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapper = mappers?.[typeof value];
|
const mapper = mappers?.[typeof value];
|
||||||
if (mapper) {
|
if (mapper) {
|
||||||
const mappedValue = mapper(value);
|
const mappedValue = mapper(value);
|
||||||
if (typeof mappedValue === typeof fallbackValue) {
|
if (typeof mappedValue === valueType) {
|
||||||
return mappedValue;
|
return mappedValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { describe, test, expect, beforeAll, afterAll } from "@jest/globals";
|
import { describe, test, expect, beforeAll, afterAll } from "@jest/globals";
|
||||||
import { setupInput, unsetInput } from "./utils/input-utils";
|
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 = {
|
const defaultInput = {
|
||||||
"boolean": true,
|
"boolean": true,
|
||||||
|
@ -195,3 +195,59 @@ describe("mapBooleanInput", () => {
|
||||||
expect(mapBooleanInput(input["booleanfalsestringuppercasewithwhitespace"], true)).toBe(false);
|
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