Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: remove ts-results-es #366

Merged
merged 6 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@
"callsites": "^3.1.0",
"cron": "^3.1.7",
"deepmerge": "^4.3.1",
"rxjs": "^7.8.0",
"ts-results-es": "^4.1.0"
"rxjs": "^7.8.0"
},
"devDependencies": {
"@faker-js/faker": "^8.0.1",
Expand Down
2 changes: 1 addition & 1 deletion src/core/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CommandType, PluginType } from './structures/enums';
import type { Plugin, PluginResult, CommandArgs, InitArgs } from '../types/core-plugin';
import { Err, Ok } from 'ts-results-es';
import { Err, Ok } from './structures/result';

export function makePlugin<V extends unknown[]>(
type: PluginType,
Expand Down
92 changes: 40 additions & 52 deletions src/core/structures/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import type {
Snowflake,
User,
} from 'discord.js';
import { CoreContext } from '../structures/core-context';
import { Result, Ok, Err } from 'ts-results-es';
import { Result, Ok, Err, val } from './result';
import * as assert from 'assert';
import type { ReplyOptions } from '../../types/utility';
import { fmt } from '../functions'
Expand All @@ -21,109 +20,105 @@ import { SernError } from './enums';
* Provides values shared between
* Message and ChatInputCommandInteraction
*/
export class Context extends CoreContext<Message, ChatInputCommandInteraction> {
export class Context {

get options() {
if(this.isMessage()) {
const [, ...rest] = fmt(this.message.content, this.prefix);
return rest;
} else {
return this.interaction.options;
}
}
return this.interaction.options;
}


protected constructor(protected ctx: Result<Message, ChatInputCommandInteraction>,
private __prefix?: string) {
super(ctx);
}
private __prefix?: string) { }
public get prefix() {
return this.__prefix;
}
public get id(): Snowflake {
return safeUnwrap(this.ctx
.map(m => m.id)
.mapErr(i => i.id));
return val(this.ctx).id
}

public get channel() {
return safeUnwrap(this.ctx.map(m => m.channel).mapErr(i => i.channel));
return val(this.ctx).channel;
}

public get channelId(): Snowflake {
return safeUnwrap(this.ctx
.map(m => m.channelId)
.mapErr(i => i.channelId));
return val(this.ctx).channelId;
}

/**
* If context is holding a message, message.author
* else, interaction.user
*/
public get user(): User {
return safeUnwrap(this.ctx
.map(m => m.author)
.mapErr(i => i.user));
if(this.ctx.ok) {
return this.ctx.value.author;
}
return this.ctx.error.user;

}

public get userId(): Snowflake {
return this.user.id;
}

public get createdTimestamp(): number {
return safeUnwrap(this.ctx
.map(m => m.createdTimestamp)
.mapErr(i => i.createdTimestamp));
return val(this.ctx).createdTimestamp;
}

public get guild() {
return safeUnwrap(this.ctx
.map(m => m.guild)
.mapErr(i => i.guild));
return val(this.ctx).guild;
}

public get guildId() {
return safeUnwrap(this.ctx
.map(m => m.guildId)
.mapErr(i => i.guildId));
return val(this.ctx).guildId;
}
/*
* interactions can return APIGuildMember if the guild it is emitted from is not cached
*/
public get member() {
return safeUnwrap(this.ctx
.map(m => m.member)
.mapErr(i => i.member));
return val(this.ctx).member;
}

get message(): Message {
return this.ctx.expect(SernError.MismatchEvent);
if(this.ctx.ok) {
return this.ctx.value;
}
throw Error(SernError.MismatchEvent);
}
public isMessage(): this is Context & { ctx: Result<Message, never> } {
return this.ctx.ok;
}

public isSlash(): this is Context & { ctx: Result<never, ChatInputCommandInteraction> } {
return !this.isMessage();
}

get interaction(): ChatInputCommandInteraction {
return this.ctx.expectErr(SernError.MismatchEvent);
if(!this.ctx.ok) {
return this.ctx.error;
}
throw Error(SernError.MismatchEvent);
}


public get client(): Client {
return safeUnwrap(this.ctx
.map(m => m.client)
.mapErr(i => i.client));
return val(this.ctx).client;
}

public get inGuild(): boolean {
return safeUnwrap(this.ctx
.map(m => m.inGuild())
.mapErr(i => i.inGuild()));
return val(this.ctx).inGuild()
}

public async reply(content: ReplyOptions) {
return safeUnwrap(
this.ctx
.map(m => m.reply(content as MessageReplyOptions))
.mapErr(i =>
i.reply(content as InteractionReplyOptions).then(() => i.fetchReply())),
);
if(this.ctx.ok) {
return this.ctx.value.reply(content as MessageReplyOptions)
}
interface FetchReply { fetchReply: true };
return this.ctx.error.reply(content as InteractionReplyOptions & FetchReply)

}

static wrap(wrappable: BaseInteraction | Message, prefix?: string): Context {
Expand All @@ -134,10 +129,3 @@ export class Context extends CoreContext<Message, ChatInputCommandInteraction> {
return new Context(Err(wrappable), prefix);
}
}

function safeUnwrap<T>(res: Result<T, T>) {
if(res.isOk()) {
return res.expect("Tried unwrapping message field: " + res)
}
return res.expectErr("Tried unwrapping interaction field" + res)
}
18 changes: 0 additions & 18 deletions src/core/structures/core-context.ts

This file was deleted.

20 changes: 20 additions & 0 deletions src/core/structures/result.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export type Result<Ok, Err> =
| { ok: true; value: Ok }
| { ok: false; error: Err };

export const Ok = <Ok>(value: Ok) => ({ ok: true, value } as const);
export const Err = <Err>(error: Err) => ({ ok: false, error } as const);

export const val = <O, E>(r: Result<O, E>) => r.ok ? r.value : r.error;
export const EMPTY_ERR = Err(undefined);

/**
* Wrap an async operation that may throw an Error (`try-catch` style) into checked exception style
* @param op The operation function
*/
export async function wrapAsync<T, E = unknown>(op: () => Promise<T>): Promise<Result<T, E>> {
try { return op()
.then(Ok)
.catch(Err); }
catch (e) { return Promise.resolve(Err(e as E)); }
}
24 changes: 13 additions & 11 deletions src/handlers/event-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import * as Id from '../core/id'
import type { Emitter, ErrorHandling, Logging } from '../core/interfaces';
import { SernError } from '../core/structures/enums'
import { Err, Ok, Result } from 'ts-results-es';
import { EMPTY_ERR, Err, Ok, Result, wrapAsync } from '../core/structures/result';
import type { UnpackedDependencies } from '../types/utility';
import type { CommandModule, Module, Processed } from '../types/core-modules';
import * as assert from 'node:assert';
Expand All @@ -17,6 +17,7 @@ import { CommandType } from '../core/structures/enums'
import { inspect } from 'node:util'
import { disposeAll } from '../core/ioc';
import { resultPayload, isAutocomplete, treeSearch, fmt } from '../core/functions'

import merge from 'deepmerge'

function handleError<C>(crashHandler: ErrorHandling, emitter: Emitter, logging?: Logging) {
Expand All @@ -43,7 +44,7 @@ interface ExecutePayload {

export const filterTap = <K, R>(onErr: (e: R) => void): OperatorFunction<Result<K, R>, K> =>
concatMap(result => {
if(result.isOk()) {
if(result.ok){
return of(result.value)
}
onErr(result.error);
Expand Down Expand Up @@ -142,7 +143,7 @@ export function createInteractionHandler<T extends Interaction>(
.map(({ id, params }) => ({ module: mg.get(id), params }))
.filter(({ module }) => module !== undefined);
if(modules.length == 0) {
return Err.EMPTY;
return EMPTY_ERR;
}
const [{module, params}] = modules;
return Ok(createDispatcher({
Expand Down Expand Up @@ -179,9 +180,9 @@ export function createMessageHandler(
* @param task the deferred execution which will be called
*/
export function executeModule(emitter: Emitter, { module, args }: ExecutePayload) {
return from(Result.wrapAsync(async () => module.execute(...args)))
return from(wrapAsync(async () => module.execute(...args)))
.pipe(concatMap(result => {
if (result.isOk()) {
if (result.ok){
emitter.emit('module.activate', resultPayload('success', module));
return EMPTY;
}
Expand All @@ -206,10 +207,10 @@ export function createResultResolver<Output>(config: {
return async (payload: ExecutePayload) => {
const task = await callPlugins(payload);
if (!task) throw Error("Plugin did not return anything.");
if(task.isOk()) {
return onNext(payload, task.value) as Output;
} else {
if(!task.ok) {
onStop?.(payload.module, String(task.error));
} else {
return onNext(payload, task.value) as Output;
}
};
};
Expand All @@ -225,12 +226,13 @@ export async function callInitPlugins(_module: Module, deps: Dependencies, emit?
for(const plugin of module.plugins ?? []) {
const result = await plugin.execute({ module, absPath: module.meta.absPath, deps });
if (!result) throw Error("Plugin did not return anything. " + inspect(plugin, false, Infinity, true));
if(result.isErr()) {
if(!result.ok) {
if(emit) {
emitter?.emit('module.register',
resultPayload('failure', module, result.error ?? SernError.PluginFailure));
}
throw Error(result.error ?? SernError.PluginFailure);
throw Error((result.error ?? SernError.PluginFailure) +
'on module ' + module.name + " " + module.meta.absPath);
}
}
return module
Expand All @@ -240,7 +242,7 @@ export async function callPlugins({ args, module, deps, params }: ExecutePayload
let state = {};
for(const plugin of module.onEvent??[]) {
const result = await plugin.execute(...args, { state, deps, params, type: module.type });
if(result.isErr()) {
if(!result.ok) {
return result;
}
if(isObject(result.value)) {
Expand Down
2 changes: 1 addition & 1 deletion src/types/core-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
* Plugins are reminiscent of middleware in express.
*/

import type { Result } from 'ts-results-es';
import type {
Module,
Processed,
Expand All @@ -31,6 +30,7 @@ import type {
UserContextMenuCommandInteraction,
UserSelectMenuInteraction,
} from 'discord.js';
import { Result } from '../core/structures/result';

export type PluginResult = Awaitable<Result<Record<string,unknown>|undefined, string|undefined>>;
export interface InitArgs<T extends Processed<Module> = Processed<Module>> {
Expand Down
2 changes: 1 addition & 1 deletion src/types/utility.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { InteractionReplyOptions, MessageReplyOptions } from 'discord.js';
import type { Module } from './core-modules';
import type { Result } from 'ts-results-es';
import type { Result } from '../core/structures/result';

export type Awaitable<T> = PromiseLike<T> | T;

Expand Down
7 changes: 0 additions & 7 deletions test/handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,6 @@ test('init plugins replace array', async () => {
expect(['a']).deep.equal(s.opts)
})

test('call control plugin ', async () => {
const plugin = CommandControlPlugin<CommandType.Slash>((ctx,sdt) => {
return controller.next();
});
const res = await plugin.execute(new ChatInputCommandInteraction(), {})
expect(res.isOk()).toBe(true)
})

test('form sdt', async () => {

Expand Down
8 changes: 0 additions & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,6 @@ __metadata:
discord.js: ^14.15.3
eslint: 8.39.0
rxjs: ^7.8.0
ts-results-es: ^4.1.0
typescript: 5.0.2
vitest: ^1.6.0
languageName: unknown
Expand Down Expand Up @@ -2959,13 +2958,6 @@ __metadata:
languageName: node
linkType: hard

"ts-results-es@npm:^4.1.0":
version: 4.2.0
resolution: "ts-results-es@npm:4.2.0"
checksum: ff475c2f6d44377e0204211e6eafdbcabddf3ad09d40540ad5dee3d817eefbd48c07a21f5ad86864ef82cd8a5542a266af9dd8dd4d58d4766fdd6e79370519bb
languageName: node
linkType: hard

"tslib@npm:2.6.2":
version: 2.6.2
resolution: "tslib@npm:2.6.2"
Expand Down
Loading