/* 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." }; };