Newer
Older
ServerGenerator / src / ExampleOutput / util.ts
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as fs from "fs";

export interface Ok<T> {
    isSuccessful: true;
    value: T;
}

export interface Err {
    isSuccessful: false;
    errorMessage: string;
}

export type Result<T> = Ok<T> | Err;

export const andThen = <T, R>(fn: (a: T) => Result<R>) => (result: Result<T>) => {
    if (result.isSuccessful) {
        return fn(result.value);
    }
    else {
        return result;
    }
}

export function parseNotNull(data: any): Result<any> {
    if (data == null) return { isSuccessful: false, errorMessage: "Input is null." };
    else return { isSuccessful: true, value: data };
}

export function parseObject(data: any): Result<Record<string, unknown>> {
    if (typeof data === "object") return { isSuccessful: true, value: data };
    else return { isSuccessful: false, errorMessage: "Input is not an object." };
}

export const parseRequiredMember = <T>(fieldName: keyof T) => (data: any): Result<any> => {
    if (fieldName in data) {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
        return { isSuccessful: true, value: data[fieldName] };
    }
    else {
        return { isSuccessful: false, errorMessage: `Required member "${fieldName.toString()}" could not be found.` };
    }
}

export const parseNullableMember = <T>(fieldName: keyof T) => (data: any): Result<any> => {
    if (fieldName in data) {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
        return { isSuccessful: true, value: data[fieldName] };
    }
    else {
        return { isSuccessful: true, value: null };
    }
}

export function parseString(data: any): Result<string> {
    if (data == null || typeof data === "string") return { isSuccessful: true, value: data };
    else return { isSuccessful: false, errorMessage: "Value is not a string." };
}

export function parseBoolean(data: any): Result<boolean> {
    if (data == null || typeof data === "boolean") return { isSuccessful: true, value: data };
    else return { isSuccessful: false, errorMessage: "Value is not a boolean." };
}

export type ParseObject<T extends {}> = {
    [P in keyof T]: Result<T[P]>
};

export function compose<R, S, T>(first: (obj:R) => Result<S>, second: (obj: S) => Result<T>): (obj:R) => Result<T> {
    return (obj:R): Result<T> => {
        const firstResult = first(obj);
        if(!firstResult.isSuccessful) return firstResult;
        return second(firstResult.value);
    };
}

export function parseArrayProperty<O, T>(fieldName: keyof O, itemParser: (item: any) => T, data: any): T[]
{
    if(fieldName in data)
    {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
        const valueList = data[fieldName];
        if(Array.isArray(valueList))
        {
            return valueList.map(itemParser);
        }
    }
    return [];
}

export function parsePrimitiveProperty<O, T = string | number>(type: T extends string ? "string" : "number", fieldName: keyof O, defaultValue: T, obj: any): T
{
    if(fieldName in obj)
    {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
        const fieldValue = obj[fieldName];
        if(typeof fieldValue === type)
        {
            // eslint-disable-next-line @typescript-eslint/no-unsafe-return
            return fieldValue;
        }
    }
    return defaultValue;
}

export function resolve<T extends {}>(members: ParseObject<T>): Result<T> {
    const obj: Partial<T> = {};
    for (const key in members) {
        if (Object.prototype.hasOwnProperty.call(members, key)) {
            const member: Result<T[Extract<keyof T, string>]> = members[key];
            if (!member.isSuccessful) {
                return member;
            }
            obj[key] = member.value;
        }
    }
    return { isSuccessful: true, value: (obj as T) };
}

export function canReadFile(path: string): boolean {
    try {
        fs.accessSync(path, fs.constants.R_OK);
        return true;
    } catch (error) {
        return false;
    }
}

function isStringValue<T extends string>(targetValue: T, value: string): value is T {
    return targetValue === value;
}

export const parseStringValue = <T extends string>(targetValue: T) => (data: string): Result<T> => {
    if(isStringValue(targetValue, data)) {
        return { isSuccessful: true, value: data };
    }
    else {
        return { isSuccessful: false, errorMessage: `String value doesn't equal '${targetValue}'.`}
    }
}

export const parseArray = <T>(itemParser: (d: unknown) => Result<T>) => (data: unknown): Result<T[]> => {
    if (Array.isArray(data)) {
        const output: T[] = [];
        for (const itemObj of data) {
            const itemRes = itemParser(itemObj);
            if (itemRes.isSuccessful) {
                output.push(itemRes.value);
            }
            else {
                return { isSuccessful: false, errorMessage: "Item could not be parsed: " + itemRes.errorMessage };
            }
        }
        return { isSuccessful: true, value: output };
    }
    return { isSuccessful: false, errorMessage: "Input is not an array." };
};