From 3612a8a86d7aefd68d03381d360affa015089c1d Mon Sep 17 00:00:00 2001 From: ton1c Date: Wed, 29 Jan 2025 01:50:26 +0300 Subject: [PATCH] working on validator --- server/src/middleware/logger.ts | 4 ++-- shared/utils/api.ts | 19 +++++++++++++------ shared/utils/option.ts | 28 +++++++++++++--------------- shared/utils/result.ts | 32 ++++++++++++++++++++++++++++++-- 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/server/src/middleware/logger.ts b/server/src/middleware/logger.ts index 8e3555b..20ede98 100644 --- a/server/src/middleware/logger.ts +++ b/server/src/middleware/logger.ts @@ -1,9 +1,9 @@ import { Middleware } from "@lib/router.ts"; const loggerMiddleware: Middleware = async (c, next) => { - console.log(c.req.method, c.path); + console.log("", c.req.method, c.path); await next(); - console.log(c.res.status); + console.log("", c.res.status, "\n"); }; export default loggerMiddleware; diff --git a/shared/utils/api.ts b/shared/utils/api.ts index 6a845a0..54e9f12 100644 --- a/shared/utils/api.ts +++ b/shared/utils/api.ts @@ -1,11 +1,18 @@ -interface LoginRequest { - password: string; +import { Result } from "@shared/utils/result.ts"; + +class ValidationError extends BaseError { + code = "ValidationError"; + constructor(msg: string) { + super(msg); + } } -interface LoginResponse { - err: "InvalidPassword" | "InvalidInput"; +class ClientApi { + constructor(path: string, method: string) {} + validate(res: Response): ResultAsync { + const body = await res.json(); + } } -class Api { - makeRequest(); +class ServerApi { } diff --git a/shared/utils/option.ts b/shared/utils/option.ts index 7149c7b..ce550f5 100644 --- a/shared/utils/option.ts +++ b/shared/utils/option.ts @@ -1,4 +1,6 @@ -import { err, ok } from "@shared/utils/result.ts"; +import { err, ok, Result } from "@shared/utils/result.ts"; + +type OptionJSON = { tag: "some"; value: T } | { tag: "none" }; interface IOption { /** @@ -64,7 +66,6 @@ interface IOption { * ``` * @param {Function} The function `fn` that takes the value inside the `Option` and returns a new `Option`. * @returns {Option} A new `Option` wrapping the result of the flatMap operation. - * */ flatMap(fn: (value: T) => Option): Option; @@ -136,6 +137,8 @@ interface IOption { toBoolean(): boolean; okOrElse(errFn: () => E): Result; + + toJSON(): OptionJSON; } /** @@ -211,17 +214,14 @@ export class Some implements IOption { return some(this.value); } - toJSON() { - return { - //_tag: this._tag, - value: this.value, - }; - } - toString() { return `Some(${this.value})`; } + toJSON(): OptionJSON { + return { tag: "some", value: this.value }; + } + toNullable(): T | null { return this.value; } @@ -307,16 +307,14 @@ export class None implements IOption { return none(); } - toJSON() { - return { - _tag: this._tag, - }; - } - toString() { return `None`; } + toJSON(): OptionJSON { + return { tag: "none" }; + } + toNullable(): T | null { return null; } diff --git a/shared/utils/result.ts b/shared/utils/result.ts index b85b4b0..4140f79 100644 --- a/shared/utils/result.ts +++ b/shared/utils/result.ts @@ -1,9 +1,8 @@ -import { some } from "@shared/utils/option.ts"; +import { none, some } from "@shared/utils/option.ts"; import { None, type Option, Some } from "@shared/utils/option.ts"; import { errAsync, okAsync, ResultAsync } from "@shared/utils/resultasync.ts"; type ResultJSON = { tag: "ok"; value: T } | { tag: "err"; error: E }; - //#region Ok, Err and Result interface IResult { isOk(): this is Ok; @@ -14,6 +13,7 @@ interface IResult { unwrap(): T; unwrapOr(defaultValue: U): T | U; unwrapOrElse(fn: () => U): T | U; + unwrapErr(): Option; match(ok: (value: T) => A, err: (error: E) => B): A | B; map(fn: (value: T) => U): Result; mapAsync(fn: (value: T) => Promise): ResultAsync; @@ -89,6 +89,10 @@ export class Ok implements IResult { return this.value; } + unwrapErr(): Option { + return none; + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars match(ok: (value: T) => A, err: (error: E) => B): A | B { return ok(this.value); @@ -219,6 +223,9 @@ export class Err implements IResult { unwrapOrElse(fn: () => U): T | U { return fn(); } + unwrapErr(): Option { + return some(this.error); + } match(ok: (value: T) => A, err: (error: E) => B): A | B { return err(this.error); } @@ -369,6 +376,27 @@ export type FlattenResult = R extends Result : R : never; +type ExtractError> = R extends + Result ? E : never; + +type CollectedErrors[]> = ExtractError< + R[number] +>; + +function collectErrors[]>( + ...results: R +): CollectedErrors[] { + const errors: CollectedErrors[] = []; + + for (const result of results) { + if (result.isErr()) { + errors.push(result.error); + } + } + + return errors; +} + class FailedToParseResult extends Error { constructor(json: string) { super(`Failed to parse ${json} as result`);