Skip to content

Commit

Permalink
refactor: better Args Result types (#501)
Browse files Browse the repository at this point in the history
* refactor: better Args Result types

* Update src/lib/structures/Argument.ts

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
  • Loading branch information
kyranet and vladfrangu committed Aug 5, 2022
1 parent c0470f5 commit b264c3e
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 28 deletions.
40 changes: 22 additions & 18 deletions src/lib/parsers/Args.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { ChannelTypes, GuildBasedChannelTypes } from '@sapphire/discord.js-utilities';
import type { ArgumentStream, Parameter } from '@sapphire/lexure';
import { container } from '@sapphire/pieces';
import { Option, Result } from '@sapphire/result';
import type { Awaitable } from '@sapphire/utilities';
import type {
CategoryChannel,
DMChannel,
Expand All @@ -14,7 +16,6 @@ import type {
User,
VoiceChannel
} from 'discord.js';
import type { ArgumentStream, Parameter } from '@sapphire/lexure';
import type { URL } from 'url';
import { ArgumentError } from '../errors/ArgumentError';
import { Identifiers } from '../errors/Identifiers';
Expand Down Expand Up @@ -87,7 +88,7 @@ export class Args {
* // Sends "The result is: 25"
* ```
*/
public async pickResult<T>(type: IArgument<T>, options?: ArgOptions): Promise<Result<T, UserError>>;
public async pickResult<T>(type: IArgument<T>, options?: ArgOptions): Promise<ResultType<T>>;
/**
* Retrieves the next parameter and parses it. Advances index on success.
* @param type The type of the argument.
Expand All @@ -104,8 +105,8 @@ export class Args {
* // Sends "The result is: 3"
* ```
*/
public async pickResult<K extends keyof ArgType>(type: K, options?: ArgOptions): Promise<Result<ArgType[K], UserError>>;
public async pickResult<K extends keyof ArgType>(type: K, options: ArgOptions = {}): Promise<Result<ArgType[K], UserError>> {
public async pickResult<K extends keyof ArgType>(type: K, options?: ArgOptions): Promise<ResultType<ArgType[K]>>;
public async pickResult<K extends keyof ArgType>(type: K, options: ArgOptions = {}): Promise<ResultType<ArgType[K]>> {
const argument = this.resolveArgument<ArgType[K]>(type);
if (!argument) return this.unavailableArgument(type);

Expand All @@ -123,7 +124,7 @@ export class Args {
return this.missingArguments();
}

return result as Result<ArgType[K], UserError>;
return result as ResultType<ArgType[K]>;
}

/**
Expand Down Expand Up @@ -176,7 +177,7 @@ export class Args {
* // Sends "The reversed value is... !dlrow olleH"
* ```
*/
public async restResult<T>(type: IArgument<T>, options?: ArgOptions): Promise<Result<T, UserError>>;
public async restResult<T>(type: IArgument<T>, options?: ArgOptions): Promise<ResultType<T>>;
/**
* Retrieves all the following arguments.
* @param type The type of the argument.
Expand All @@ -193,8 +194,8 @@ export class Args {
* // Sends "The repeated value is... Hello World!Hello World!"
* ```
*/
public async restResult<K extends keyof ArgType>(type: K, options?: ArgOptions): Promise<Result<ArgType[K], UserError>>;
public async restResult<T>(type: keyof ArgType | IArgument<T>, options: ArgOptions = {}): Promise<Result<unknown, UserError>> {
public async restResult<K extends keyof ArgType>(type: K, options?: ArgOptions): Promise<ResultType<ArgType[K]>>;
public async restResult<T>(type: keyof ArgType | IArgument<T>, options: ArgOptions = {}): Promise<ResultType<T>> {
const argument = this.resolveArgument(type);
if (!argument) return this.unavailableArgument(type);
if (this.parser.finished) return this.missingArguments();
Expand Down Expand Up @@ -261,7 +262,7 @@ export class Args {
* // Sends "You have written 2 word(s): olleH !dlroW"
* ```
*/
public async repeatResult<T>(type: IArgument<T>, options?: RepeatArgOptions): Promise<Result<T[], UserError>>;
public async repeatResult<T>(type: IArgument<T>, options?: RepeatArgOptions): Promise<ArrayResultType<T>>;
/**
* Retrieves all the following arguments.
* @param type The type of the argument.
Expand All @@ -275,8 +276,8 @@ export class Args {
* // Sends "You have written 2 word(s): Hello World!"
* ```
*/
public async repeatResult<K extends keyof ArgType>(type: K, options?: RepeatArgOptions): Promise<Result<ArgType[K][], UserError>>;
public async repeatResult<K extends keyof ArgType>(type: K, options: RepeatArgOptions = {}): Promise<Result<ArgType[K][], UserError>> {
public async repeatResult<K extends keyof ArgType>(type: K, options?: RepeatArgOptions): Promise<ArrayResultType<ArgType[K]>>;
public async repeatResult<K extends keyof ArgType>(type: K, options: RepeatArgOptions = {}): Promise<ArrayResultType<ArgType[K]>> {
const argument = this.resolveArgument(type);
if (!argument) return this.unavailableArgument(type);
if (this.parser.finished) return this.missingArguments();
Expand All @@ -298,7 +299,7 @@ export class Args {
if (error === null) break;

if (output.length === 0) {
return result as Result.Err<UserError>;
return result as Result.Err<UserError | ArgumentError<ArgType[K]>>;
}

break;
Expand Down Expand Up @@ -360,7 +361,7 @@ export class Args {
* if (isOk(firstWord)) await message.channel.send(firstWord.value.toUpperCase()); // HELLO
* ```
*/
public async peekResult<T>(type: () => Argument.Result<T>): Promise<Result<T, UserError>>;
public async peekResult<T>(type: () => Argument.Result<T>): Promise<ResultType<T>>;
/**
* Peeks the following parameter(s) without advancing the parser's state.
* Passing a function as a parameter allows for returning {@link Args.pickResult}, {@link Args.repeatResult},
Expand All @@ -379,7 +380,7 @@ export class Args {
* if (isOk(firstWord)) await message.channel.send(firstWord.value.toUpperCase()); // SAPPHIRE
* ```
*/
public async peekResult<T>(type: IArgument<T>, options?: ArgOptions): Promise<Result<T, UserError>>;
public async peekResult<T>(type: IArgument<T>, options?: ArgOptions): Promise<ResultType<T>>;
/**
* Peeks the following parameter(s) without advancing the parser's state.
* Passing a function as a parameter allows for returning {@link Args.pickResult}, {@link Args.repeatResult},
Expand All @@ -399,14 +400,14 @@ export class Args {
* ```
*/
public async peekResult<K extends keyof ArgType>(
type: (() => Argument.Result<ArgType[K]>) | K,
type: (() => Awaitable<Argument.Result<ArgType[K]>>) | K,
options?: ArgOptions
): Promise<Result<ArgType[K], UserError>>;
): Promise<ResultType<ArgType[K]>>;

public async peekResult<K extends keyof ArgType>(
type: (() => Argument.Result<ArgType[K]>) | K,
type: (() => Awaitable<Argument.Result<ArgType[K]>>) | K,
options: ArgOptions = {}
): Promise<Result<ArgType[K], UserError>> {
): Promise<ResultType<ArgType[K]>> {
this.save();
const result = typeof type === 'function' ? await type() : await this.pickResult(type, options);
this.restore();
Expand Down Expand Up @@ -732,3 +733,6 @@ export interface ArgsNextCallback<T> {
*/
(value: string): Option<T>;
}

export type ResultType<T> = Result<T, UserError | ArgumentError<T>>;
export type ArrayResultType<T> = Result<T[], UserError | ArgumentError<T>>;
23 changes: 13 additions & 10 deletions src/lib/structures/Argument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@ import type { Result } from '@sapphire/result';
import type { Awaitable } from '@sapphire/utilities';
import type { Message } from 'discord.js';
import type { ArgumentError } from '../errors/ArgumentError';
import type { UserError } from '../errors/UserError';
import { Args } from '../parsers/Args';
import type { MessageCommand } from './Command';

/**
* Defines a synchronous result of an {@link Argument}, check {@link Argument.AsyncResult} for the asynchronous version.
*/
export type ArgumentResult<T> = Awaitable<Result<T, UserError>>;
export type ArgumentResult<T> = Result<T, ArgumentError<T>>;

/**
* Defines a synchronous or asynchronous result of an {@link Argument}, check {@link Argument.AsyncResult} for the asynchronous version.
*/
export type AwaitableArgumentResult<T> = Awaitable<ArgumentResult<T>>;

/**
* Defines an asynchronous result of an {@link Argument}, check {@link Argument.Result} for the synchronous version.
*/
export type AsyncArgumentResult<T> = Promise<Result<T, UserError>>;
export type AsyncArgumentResult<T> = Promise<ArgumentResult<T>>;

export interface IArgument<T> {
/**
Expand All @@ -28,7 +32,7 @@ export interface IArgument<T> {
* @param parameter The string parameter to parse.
* @param context The context for the method call, contains the message, command, and other options.
*/
run(parameter: string, context: Argument.Context<T>): Argument.Result<T>;
run(parameter: string, context: Argument.Context<T>): Argument.AwaitableResult<T>;
}

/**
Expand Down Expand Up @@ -88,23 +92,21 @@ export interface IArgument<T> {
* ```
*/
export abstract class Argument<T = unknown, O extends Argument.Options = Argument.Options> extends AliasPiece<O> implements IArgument<T> {
public abstract run(parameter: string, context: Argument.Context<T>): Argument.Result<T>;
public abstract run(parameter: string, context: Argument.Context<T>): Argument.AwaitableResult<T>;

/**
* Wraps a value into a successful value.
* @param value The value to wrap.
*/
public ok(value: T): Result<T, UserError> {
public ok(value: T): Argument.Result<T> {
return Args.ok(value);
}

/**
* Constructs an {@link Err} result containing an {@link ArgumentError} with a custom type.
* @param parameter The parameter that triggered the argument.
* @param type The identifier for the error.
* @param message The description message for the rejection.
* @param options The options to pass to the ArgumentError.
*/
public error(options: Omit<ArgumentError.Options<T>, 'argument'>): Result<T, UserError> {
public error(options: Omit<ArgumentError.Options<T>, 'argument'>): Argument.Result<T> {
return Args.error({ argument: this, identifier: this.name, ...options });
}
}
Expand All @@ -126,5 +128,6 @@ export namespace Argument {
export type Options = ArgumentOptions;
export type Context<T = unknown> = ArgumentContext<T>;
export type Result<T> = ArgumentResult<T>;
export type AwaitableResult<T> = AwaitableArgumentResult<T>;
export type AsyncResult<T> = AsyncArgumentResult<T>;
}

0 comments on commit b264c3e

Please sign in to comment.