mirror of
https://github.com/the-draupnir-project/Draupnir.git
synced 2026-10-23 17:32:22 +00:00
Update all of InterfaceManager to use MPS.
This commit is contained in:
1 parent
98f3471ea0
commit
68bfd61ccf
10 files changed
+113
-113
No files matched your search
@@ -3,7 +3,7 @@
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
import { MatrixSendClient } from "../../MatrixEmitter";
|
||||
import { MatrixSendClient } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
import { AbstractNode, DocumentNode, FringeWalker, NodeTag } from "./DeadDocument";
|
||||
import { HTML_RENDERER } from "./DeadDocumentHtml";
|
||||
import { MARKDOWN_RENDERER } from "./DeadDocumentMarkdown";
|
||||
|
||||
@@ -29,8 +29,8 @@ limitations under the License.
|
||||
* I'd like to remove the dependency on matrix-bot-sdk.
|
||||
*/
|
||||
|
||||
import { ActionResult, isError } from "matrix-protection-suite";
|
||||
import { ParameterParser, IArgumentStream, IArgumentListParser, ParsedKeywords, ArgumentStream } from "./ParameterParsing";
|
||||
import { CommandResult } from "./Validation";
|
||||
|
||||
/**
|
||||
* 💀 . o O ( at least I don't have to remember the types )
|
||||
@@ -38,7 +38,7 @@ import { CommandResult } from "./Validation";
|
||||
* Probably am "doing something wrong", and no, trying to make this protocol isn't it.
|
||||
*/
|
||||
|
||||
export type BaseFunction = (keywords: ParsedKeywords, ...args: any) => Promise<CommandResult<any>>;
|
||||
export type BaseFunction = (keywords: ParsedKeywords, ...args: any) => Promise<ActionResult<any>>;
|
||||
|
||||
type CommandLookupEntry<ExecutorType extends BaseFunction> = {
|
||||
next?: Map<string, CommandLookupEntry<ExecutorType>>,
|
||||
@@ -200,7 +200,7 @@ export class InterfaceCommand<ExecutorType extends BaseFunction = BaseFunction>
|
||||
|
||||
public async parseThenInvoke(context: ThisParameterType<ExecutorType>, stream: IArgumentStream): Promise<ReturnType<ExecutorType>> {
|
||||
const parameterDescription = await this.parseArguments(stream);
|
||||
if (parameterDescription.isErr()) {
|
||||
if (isError(parameterDescription)) {
|
||||
// The inner type is irrelevant when it is Err, i don't know how to encode this in TS's type system but whatever.
|
||||
return parameterDescription as ReturnType<Awaited<ExecutorType>>;
|
||||
}
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
* Copyright (C) 2022 Gnuxie <Gnuxie@protonmail.com>
|
||||
*/
|
||||
|
||||
import { MatrixSendClient } from "../../MatrixEmitter";
|
||||
import { BaseFunction, CommandTable, InterfaceCommand } from "./InterfaceCommand";
|
||||
import { MatrixContext, MatrixInterfaceAdaptor } from "./MatrixInterfaceAdaptor";
|
||||
import { ArgumentParseError, ParameterDescription, RestDescription } from "./ParameterParsing";
|
||||
import { CommandError, CommandResult } from "./Validation";
|
||||
import { JSXFactory } from "./JSXFactory";
|
||||
import { DocumentNode } from "./DeadDocument";
|
||||
import { renderMatrixAndSend } from "./DeadDocumentMatrix";
|
||||
import { CommandException } from "./CommandException";
|
||||
import { LogService } from "matrix-bot-sdk";
|
||||
import { MatrixSendClient } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
import { ActionException, ActionResult, StringRoomID, isError } from "matrix-protection-suite";
|
||||
|
||||
function requiredArgument(argumentName: string): string {
|
||||
return `<${argumentName}>`;
|
||||
@@ -77,45 +76,45 @@ function renderTableHelp(table: CommandTable): DocumentNode {
|
||||
</root>
|
||||
}
|
||||
|
||||
export async function renderHelp(client: MatrixSendClient, commandRoomId: string, event: any, result: CommandResult<CommandTable, CommandError>): Promise<void> {
|
||||
if (result.isErr()) {
|
||||
export async function renderHelp(client: MatrixSendClient, commandRoomID: StringRoomID, event: any, result: ActionResult<CommandTable>): Promise<void> {
|
||||
if (isError(result)) {
|
||||
throw new TypeError("This command isn't supposed to fail");
|
||||
}
|
||||
await renderMatrixAndSend(
|
||||
renderTableHelp(result.ok),
|
||||
commandRoomId,
|
||||
commandRoomID,
|
||||
event,
|
||||
client
|
||||
);
|
||||
}
|
||||
|
||||
export async function tickCrossRenderer(this: MatrixInterfaceAdaptor<MatrixContext, BaseFunction>, client: MatrixSendClient, commandRoomId: string, event: any, result: CommandResult<unknown, CommandError>): Promise<void> {
|
||||
export async function tickCrossRenderer(this: MatrixInterfaceAdaptor<MatrixContext, BaseFunction>, client: MatrixSendClient, commandRoomID: StringRoomID, event: any, result: ActionResult<unknown>): Promise<void> {
|
||||
const react = async (emote: string) => {
|
||||
try {
|
||||
await client.unstableApis.addReactionToEvent(commandRoomId, event['event_id'], emote);
|
||||
await client.unstableApis.addReactionToEvent(commandRoomID, event['event_id'], emote);
|
||||
} catch (e) {
|
||||
LogService.error("tickCrossRenderer", "Couldn't react to the event", event['event_id'], e);
|
||||
}
|
||||
}
|
||||
if (result.isOk()) {
|
||||
if (result.isOkay) {
|
||||
await react('✅')
|
||||
} else {
|
||||
if (result.err instanceof ArgumentParseError) {
|
||||
if (result.error instanceof ArgumentParseError) {
|
||||
await renderMatrixAndSend(
|
||||
renderArgumentParseError(this.interfaceCommand, result.err),
|
||||
commandRoomId,
|
||||
renderArgumentParseError(this.interfaceCommand, result.error),
|
||||
commandRoomID,
|
||||
event,
|
||||
client);
|
||||
} else if (result.err instanceof CommandException) {
|
||||
const commandError = result.err;
|
||||
} else if (result.error instanceof ActionException) {
|
||||
const commandError = result.error;
|
||||
LogService.error("CommandException", commandError.uuid, commandError.message, commandError.exception);
|
||||
await renderMatrixAndSend(
|
||||
renderCommandException(this.interfaceCommand, result.err),
|
||||
commandRoomId,
|
||||
renderCommandException(this.interfaceCommand, result.error),
|
||||
commandRoomID,
|
||||
event,
|
||||
client);
|
||||
} else {
|
||||
await client.replyNotice(commandRoomId, event, result.err.message);
|
||||
await client.replyNotice(commandRoomID, event, result.error.message);
|
||||
}
|
||||
// reacting is way less important than communicating what happened, do it last.
|
||||
await react('❌');
|
||||
@@ -146,7 +145,7 @@ function renderArgumentParseError(command: InterfaceCommand<BaseFunction>, error
|
||||
</root>
|
||||
}
|
||||
|
||||
function renderCommandException(command: InterfaceCommand<BaseFunction>, error: CommandException): DocumentNode {
|
||||
function renderCommandException(command: InterfaceCommand<BaseFunction>, error: ActionException): DocumentNode {
|
||||
return <root>
|
||||
There was an unexpected error when processing this command:<br />
|
||||
{error.message}<br />
|
||||
|
||||
@@ -29,28 +29,28 @@ limitations under the License.
|
||||
* I'd like to remove the dependency on matrix-bot-sdk.
|
||||
*/
|
||||
|
||||
import { CommandError, CommandResult } from "./Validation";
|
||||
import { LogService, MatrixClient } from "matrix-bot-sdk";
|
||||
import { ReadItem } from "./CommandReader";
|
||||
import { MatrixEmitter, MatrixSendClient } from "../../MatrixEmitter";
|
||||
import { BaseFunction, InterfaceCommand } from "./InterfaceCommand";
|
||||
import { tickCrossRenderer } from "./MatrixHelpRenderer";
|
||||
import { CommandInvocationRecord, InterfaceAcceptor, PromptableArgumentStream, PromptOptions } from "./PromptForAccept";
|
||||
import { ParameterDescription } from "./ParameterParsing";
|
||||
import { matrixPromptForAccept } from "./MatrixPromptForAccept";
|
||||
import { MatrixSendClient, SafeMatrixEmitter } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
import { ActionError, ActionResult, ResultError, RoomEvent, RoomMessage, StringRoomID, isError } from "matrix-protection-suite";
|
||||
|
||||
export interface MatrixContext {
|
||||
client: MatrixSendClient,
|
||||
emitter: MatrixEmitter,
|
||||
roomId: string,
|
||||
event: any,
|
||||
emitter: SafeMatrixEmitter,
|
||||
roomID: StringRoomID,
|
||||
event: RoomMessage,
|
||||
}
|
||||
|
||||
type RendererSignature<C extends MatrixContext, ExecutorType extends BaseFunction> = (
|
||||
this: MatrixInterfaceAdaptor<C, ExecutorType>,
|
||||
client: MatrixClient,
|
||||
commandRoomId: string,
|
||||
event: any,
|
||||
event: RoomEvent,
|
||||
result: Awaited<ReturnType<ExecutorType>>) => Promise<void>;
|
||||
|
||||
export class MatrixInterfaceAdaptor<C extends MatrixContext, ExecutorType extends BaseFunction = BaseFunction> implements InterfaceAcceptor {
|
||||
@@ -58,7 +58,7 @@ export class MatrixInterfaceAdaptor<C extends MatrixContext, ExecutorType extend
|
||||
constructor(
|
||||
public readonly interfaceCommand: InterfaceCommand<ExecutorType>,
|
||||
private readonly renderer: RendererSignature<C, ExecutorType>,
|
||||
private readonly validationErrorHandler?: (client: MatrixClient, roomId: string, event: any, validationError: CommandError) => Promise<void>
|
||||
private readonly validationErrorHandler?: (client: MatrixClient, roomID: StringRoomID, event: RoomEvent, validationError: ActionError) => Promise<void>
|
||||
) {
|
||||
|
||||
}
|
||||
@@ -75,13 +75,13 @@ export class MatrixInterfaceAdaptor<C extends MatrixContext, ExecutorType extend
|
||||
const invocationRecord = new MatrixInvocationRecord<ThisParameterType<ExecutorType>>(this.interfaceCommand, executorContext, matrixContext);
|
||||
const stream = new PromptableArgumentStream(args, this, invocationRecord);
|
||||
const executorResult: Awaited<ReturnType<typeof this.interfaceCommand.parseThenInvoke>> = await this.interfaceCommand.parseThenInvoke(executorContext, stream);
|
||||
if (executorResult.isErr()) {
|
||||
this.reportValidationError(matrixContext.client, matrixContext.roomId, matrixContext.event, executorResult.err);
|
||||
if (isError(executorResult)) {
|
||||
this.reportValidationError(matrixContext.client, matrixContext.roomID, matrixContext.event, executorResult.error);
|
||||
return;
|
||||
}
|
||||
// just give the renderer the MatrixContext.
|
||||
// we need to give the renderer the command itself!
|
||||
await this.renderer.apply(this, [matrixContext.client, matrixContext.roomId, matrixContext.event, executorResult]);
|
||||
await this.renderer.apply(this, [matrixContext.client, matrixContext.roomID, matrixContext.event, executorResult]);
|
||||
}
|
||||
|
||||
// is this still necessary, surely this should be handled entirely by the renderer?
|
||||
@@ -89,16 +89,16 @@ export class MatrixInterfaceAdaptor<C extends MatrixContext, ExecutorType extend
|
||||
// and an error discovered because their is a fault or an error running the command. Though i don't think this is correct
|
||||
// since any CommandError recieved is an expected error. It means there is no fault. An exception on the other hand does
|
||||
// so this suggests we should just remove this.
|
||||
private async reportValidationError(client: MatrixSendClient, roomId: string, event: any, validationError: CommandError): Promise<void> {
|
||||
private async reportValidationError(client: MatrixSendClient, roomID: StringRoomID, event: RoomMessage, validationError: ActionError): Promise<void> {
|
||||
LogService.info("MatrixInterfaceCommand", `User input validation error when parsing command ${JSON.stringify(this.interfaceCommand.designator)}: ${validationError.message}`);
|
||||
if (this.validationErrorHandler) {
|
||||
await this.validationErrorHandler.apply(this, arguments);
|
||||
return;
|
||||
}
|
||||
await tickCrossRenderer.call(this, client, roomId, event, CommandResult.Err(validationError));
|
||||
await tickCrossRenderer.call(this, client, roomID, event, ResultError(validationError));
|
||||
}
|
||||
|
||||
public async promptForAccept<PresentationType = unknown>(parameter: ParameterDescription, invocationRecord: CommandInvocationRecord): Promise<CommandResult<PresentationType>> {
|
||||
public async promptForAccept<PresentationType = unknown>(parameter: ParameterDescription, invocationRecord: CommandInvocationRecord): Promise<ActionResult<PresentationType>> {
|
||||
if (!(invocationRecord instanceof MatrixInvocationRecord)) {
|
||||
throw new TypeError("The MatrixInterfaceAdaptor only supports invocation records that were produced by itself.");
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
import { ReadItem } from "./CommandReader";
|
||||
import { findPresentationType, makePresentationType, simpleTypeValidator } from "./ParameterParsing";
|
||||
import { UserID } from "matrix-bot-sdk";
|
||||
import { MatrixRoomAlias, MatrixRoomID, MatrixRoomReference } from "./MatrixRoomReference";
|
||||
import { definePresentationRenderer } from "./DeadDocumentPresentation";
|
||||
import { JSXFactory } from "./JSXFactory";
|
||||
import { DocumentNode } from "./DeadDocument";
|
||||
import { MatrixRoomAlias, MatrixRoomID } from "matrix-protection-suite";
|
||||
|
||||
|
||||
makePresentationType({
|
||||
@@ -19,7 +19,7 @@ makePresentationType({
|
||||
|
||||
makePresentationType({
|
||||
name: 'MatrixRoomReference',
|
||||
validator: simpleTypeValidator('MatrixRoomReference', (item: ReadItem) => item instanceof MatrixRoomReference),
|
||||
validator: simpleTypeValidator('MatrixRoomReference', (item: ReadItem) => item instanceof MatrixRoomID || item instanceof MatrixRoomAlias),
|
||||
})
|
||||
|
||||
makePresentationType({
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
import { ActionResult, StringUserID } from "matrix-protection-suite";
|
||||
import { renderMatrixAndSend } from "./DeadDocumentMatrix";
|
||||
import { BaseFunction, InterfaceCommand } from "./InterfaceCommand";
|
||||
import { JSXFactory } from "./JSXFactory";
|
||||
@@ -10,7 +11,6 @@ import { MatrixContext } from "./MatrixInterfaceAdaptor";
|
||||
import { PromptResponseListener } from "./MatrixPromptUX";
|
||||
import { ParameterDescription } from "./ParameterParsing";
|
||||
import { PromptOptions } from "./PromptForAccept";
|
||||
import { CommandResult } from "./Validation";
|
||||
|
||||
async function promptDefault<PresentationType>(this: MatrixContext, parameter: ParameterDescription, command: InterfaceCommand<BaseFunction>, defaultPrompt: PresentationType) {
|
||||
await renderMatrixAndSend(
|
||||
@@ -18,7 +18,7 @@ async function promptDefault<PresentationType>(this: MatrixContext, parameter: P
|
||||
No argument was provided for the parameter {parameter.name}, would you like to accept the default?<br/>
|
||||
{defaultPrompt}
|
||||
</root>,
|
||||
this.roomId, this.event, this.client
|
||||
this.roomID, this.event, this.client
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,22 +38,23 @@ async function promptSuggestions<PresentationType>(
|
||||
})}
|
||||
</ol>
|
||||
</root>,
|
||||
this.roomId, this.event, this.client
|
||||
this.roomID, this.event, this.client
|
||||
)).at(0) as string;
|
||||
|
||||
}
|
||||
|
||||
export async function matrixPromptForAccept<PresentationType = any> (
|
||||
this: MatrixContext, parameter: ParameterDescription, command: InterfaceCommand<BaseFunction>, promptOptions: PromptOptions
|
||||
): Promise<CommandResult<PresentationType>> {
|
||||
const promptHelper = new PromptResponseListener(this.emitter, await this.client.getUserId(), this.client);
|
||||
): Promise<ActionResult<PresentationType>> {
|
||||
// FIXME: is there a better way to get the clinet ID? why isn't Draupnir in the command context?
|
||||
const promptHelper = new PromptResponseListener(this.emitter, await this.client.getUserId() as StringUserID, this.client);
|
||||
if (promptOptions.default) {
|
||||
await promptDefault.call(this, parameter, command, promptOptions.default);
|
||||
throw new TypeError("default prompts are not implemented yet.");
|
||||
}
|
||||
return await promptHelper.waitForPresentationList<PresentationType>(
|
||||
promptOptions.suggestions,
|
||||
this.roomId,
|
||||
this.roomID,
|
||||
promptSuggestions.call(this, parameter, command, promptOptions.suggestions)
|
||||
);
|
||||
}
|
||||
@@ -3,9 +3,9 @@
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
import { MatrixEmitter, MatrixSendClient } from "../../MatrixEmitter";
|
||||
import { CommandError, CommandResult } from "./Validation";
|
||||
import { LogService } from "matrix-bot-sdk";
|
||||
import { ActionError, ActionResult, Ok, StringUserID } from "matrix-protection-suite";
|
||||
import { MatrixSendClient, SafeMatrixEmitter } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
|
||||
type PresentationByReactionKey = Map<string/*reaction key*/, any/*presentation*/>;
|
||||
|
||||
@@ -29,8 +29,8 @@ class ReactionHandler {
|
||||
private readonly promptRecordByEvent: Map<string/*event id*/, Set<ReactionPromptRecord>> = new Map();
|
||||
|
||||
constructor(
|
||||
matrixEmitter: MatrixEmitter,
|
||||
private readonly userId: string,
|
||||
matrixEmitter: SafeMatrixEmitter,
|
||||
private readonly userID: StringUserID,
|
||||
private readonly client: MatrixSendClient,
|
||||
) {
|
||||
matrixEmitter.on('room.event', this.handleEvent.bind(this))
|
||||
@@ -83,7 +83,7 @@ class ReactionHandler {
|
||||
if (!(typeof relatedEventId === 'string' && typeof reactionKey === 'string')) {
|
||||
return;
|
||||
}
|
||||
if (event['sender'] === this.userId) {
|
||||
if (event['sender'] === this.userID) {
|
||||
return;
|
||||
}
|
||||
const entry = this.promptRecordByEvent.get(relatedEventId);
|
||||
@@ -104,7 +104,7 @@ class ReactionHandler {
|
||||
|
||||
public async waitForReactionToPrompt<T>(
|
||||
roomId: string, eventId: string, presentationByReaction: PresentationByReactionKey, timeout = 600_000 // ten minutes
|
||||
): Promise<CommandResult<T, CommandError>> {
|
||||
): Promise<ActionResult<T>> {
|
||||
let record;
|
||||
let timeoutId;
|
||||
const presentationOrTimeout = await Promise.race([
|
||||
@@ -120,9 +120,9 @@ class ReactionHandler {
|
||||
if (record !== undefined) {
|
||||
this.removePromptRecordForEvent(eventId, record);
|
||||
}
|
||||
return CommandError.Result(`Timed out while waiting for a response to the prompt`);
|
||||
return ActionError.Result(`Timed out while waiting for a response to the prompt`);
|
||||
} else {
|
||||
return CommandResult.Ok(presentationOrTimeout as T);
|
||||
return Ok(presentationOrTimeout as T);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,11 +154,11 @@ export class PromptResponseListener {
|
||||
private readonly reactionHandler: ReactionHandler;
|
||||
|
||||
constructor(
|
||||
matrixEmitter: MatrixEmitter,
|
||||
userId: string,
|
||||
matrixEmitter: SafeMatrixEmitter,
|
||||
userID: StringUserID,
|
||||
client: MatrixSendClient,
|
||||
) {
|
||||
this.reactionHandler = new ReactionHandler(matrixEmitter, userId, client);
|
||||
this.reactionHandler = new ReactionHandler(matrixEmitter, userID, client);
|
||||
}
|
||||
|
||||
private indexToReactionKey(index: number): string {
|
||||
@@ -168,7 +168,7 @@ export class PromptResponseListener {
|
||||
// This won't work, we have to have a special key in the original event
|
||||
// that means we should be waiting for it, that can't be abused/forged.
|
||||
// As we can't have the event id AOT.
|
||||
public async waitForPresentationList<T>(presentations: T[], roomId: string, eventPromise: Promise<string>): Promise<CommandResult<T>> {
|
||||
public async waitForPresentationList<T>(presentations: T[], roomId: string, eventPromise: Promise<string>): Promise<ActionResult<T>> {
|
||||
const presentationByReactionKey = presentations.reduce(
|
||||
(map: PresentationByReactionKey, presentation: T, index: number) => {
|
||||
return map.set(this.indexToReactionKey(index), presentation);
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
|
||||
import { EventEmitter } from "stream";
|
||||
import { MatrixEmitter, MatrixSendClient } from "../../MatrixEmitter";
|
||||
import { LogService } from "matrix-bot-sdk";
|
||||
import { MatrixSendClient, SafeMatrixEmitter } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
|
||||
const REACTION_ANNOTATION_KEY = 'ge.applied-langua.ge.draupnir.reaction_handler';
|
||||
|
||||
@@ -92,14 +92,14 @@ export class MatrixReactionHandler extends EventEmitter {
|
||||
* Start listening for reactions to events.
|
||||
* Called normally by an associated mjolnir instance when it is started.
|
||||
*/
|
||||
public start(emitter: MatrixEmitter): void {
|
||||
public start(emitter: SafeMatrixEmitter): void {
|
||||
emitter.on('room.event', this.listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening for reactions to events.
|
||||
*/
|
||||
public stop(emitter: MatrixEmitter): void {
|
||||
public stop(emitter: SafeMatrixEmitter): void {
|
||||
emitter.off('room.event', this.listener);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,15 +24,15 @@ limitations under the License.
|
||||
* are NOT distributed, contributed, or committed under the Apache License.
|
||||
*/
|
||||
|
||||
import { ActionError, ActionResult, Ok, ResultError, isError } from "matrix-protection-suite";
|
||||
import { ISuperCoolStream, Keyword, ReadItem, SuperCoolStream } from "./CommandReader";
|
||||
import { PromptOptions } from "./PromptForAccept";
|
||||
import { CommandError, CommandResult } from "./Validation";
|
||||
|
||||
export interface IArgumentStream extends ISuperCoolStream<ReadItem[]> {
|
||||
rest(): ReadItem[],
|
||||
isPromptable(): boolean,
|
||||
// should prompt really return a new stream?
|
||||
prompt(parameterDescription: ParameterDescription): Promise<CommandResult<ReadItem>>,
|
||||
prompt(parameterDescription: ParameterDescription): Promise<ActionResult<ReadItem>>,
|
||||
}
|
||||
|
||||
export class ArgumentStream extends SuperCoolStream<ReadItem[]> implements IArgumentStream {
|
||||
@@ -44,7 +44,7 @@ export class ArgumentStream extends SuperCoolStream<ReadItem[]> implements IArgu
|
||||
return false;
|
||||
}
|
||||
|
||||
prompt(parameterDescription: ParameterDescription): Promise<CommandResult<ReadItem>> {
|
||||
prompt(parameterDescription: ParameterDescription): Promise<ActionResult<ReadItem>> {
|
||||
throw new TypeError("This argument stream is NOT promptable, did you even check isPromptable().");
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export class ArgumentStream extends SuperCoolStream<ReadItem[]> implements IArgu
|
||||
// TODO: Presentation types should be extracted to their own file.
|
||||
// FIXME: PresentationTypes should not be limited to ReadItems.
|
||||
|
||||
export type PredicateIsParameter = (readItem: ReadItem) => CommandResult<true>;
|
||||
export type PredicateIsParameter = (readItem: ReadItem) => ActionResult<true>;
|
||||
|
||||
export interface PresentationType {
|
||||
validator: PredicateIsParameter,
|
||||
@@ -86,10 +86,10 @@ export function simpleTypeValidator(name: string, predicate: (readItem: ReadItem
|
||||
return (readItem: ReadItem) => {
|
||||
const result = predicate(readItem);
|
||||
if (result) {
|
||||
return CommandResult.Ok(result);
|
||||
return Ok(result);
|
||||
} else {
|
||||
// How do we accurately denote the type when it includes spaces in its name, same for the read item?
|
||||
return CommandError.Result(`Was expecting a match for the presentation type: ${name} but got ${readItem}.`);
|
||||
return ActionError.Result(`Was expecting a match for the presentation type: ${name} but got ${readItem}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ export function presentationTypeOf(presentation: unknown): PresentationType|unde
|
||||
// We have no concept of presentation-subtype
|
||||
// But we have a top type which is any...
|
||||
const candidates = [...PRESENTATION_TYPES.values()]
|
||||
.filter(possibleType => possibleType.validator(presentation as ReadItem).isOk()
|
||||
.filter(possibleType => possibleType.validator(presentation as ReadItem).isOkay
|
||||
&& possibleType.name !== 'any'
|
||||
);
|
||||
if (candidates.length === 0) {
|
||||
@@ -154,34 +154,34 @@ export class RestDescription<ExecutorContext = unknown> implements ParameterDesc
|
||||
* Parse the rest of a command.
|
||||
* @param stream An argument stream that starts at the rest of a command.
|
||||
* @param keywordParser Used to store any keywords found in the rest of the command.
|
||||
* @returns A CommandResult of ReadItems associated with the rest of the command.
|
||||
* @returns A ActionResult of ReadItems associated with the rest of the command.
|
||||
* If a ReadItem or Keyword is invalid for the command, then an error will be returned.
|
||||
*/
|
||||
public async parseRest(stream: IArgumentStream, promptForRest: boolean, keywordParser: KeywordParser): Promise<CommandResult<ReadItem[]>> {
|
||||
public async parseRest(stream: IArgumentStream, promptForRest: boolean, keywordParser: KeywordParser): Promise<ActionResult<ReadItem[]>> {
|
||||
const items: ReadItem[] = [];
|
||||
if (this.prompt && promptForRest && stream.isPromptable() && stream.peekItem() === undefined) {
|
||||
const result = await stream.prompt(this);
|
||||
if (result.isErr()) {
|
||||
return result as any;
|
||||
if (isError(result)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
while (stream.peekItem() !== undefined) {
|
||||
const keywordResult = keywordParser.parseKeywords(stream);
|
||||
if (keywordResult.isErr()) {
|
||||
return CommandResult.Err(keywordResult.err);
|
||||
if (isError(keywordResult)) {
|
||||
return keywordResult;
|
||||
}
|
||||
if (stream.peekItem() !== undefined) {
|
||||
const validationResult = this.acceptor.validator(stream.peekItem());
|
||||
if (validationResult.isErr()) {
|
||||
if (isError(validationResult)) {
|
||||
return ArgumentParseError.Result(
|
||||
validationResult.err.message,
|
||||
validationResult.error.message,
|
||||
{ parameter: this, stream }
|
||||
);
|
||||
}
|
||||
items.push(stream.readItem());
|
||||
}
|
||||
}
|
||||
return CommandResult.Ok(items);
|
||||
return Ok(items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,24 +263,24 @@ class KeywordParser {
|
||||
}
|
||||
|
||||
|
||||
private readKeywordAssociatedProperty(keyword: KeywordPropertyDescription, itemStream: IArgumentStream): CommandResult<any, ArgumentParseError> {
|
||||
private readKeywordAssociatedProperty(keyword: KeywordPropertyDescription, itemStream: IArgumentStream): ActionResult<any, ArgumentParseError> {
|
||||
if (itemStream.peekItem() !== undefined && !(itemStream.peekItem() instanceof Keyword)) {
|
||||
const validationResult = keyword.acceptor.validator(itemStream.peekItem());
|
||||
if (validationResult.isOk()) {
|
||||
return CommandResult.Ok(itemStream.readItem());
|
||||
if (validationResult.isOkay) {
|
||||
return Ok(itemStream.readItem());
|
||||
} else {
|
||||
return ArgumentParseError.Result(validationResult.err.message, { parameter: keyword, stream: itemStream });
|
||||
return ArgumentParseError.Result(validationResult.error.message, { parameter: keyword, stream: itemStream });
|
||||
}
|
||||
} else {
|
||||
if (!keyword.isFlag) {
|
||||
return ArgumentParseError.Result(`An associated argument was not provided for the keyword ${keyword.name}.`, { parameter: keyword, stream: itemStream });
|
||||
} else {
|
||||
return CommandResult.Ok(true);
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public parseKeywords(itemStream: IArgumentStream): CommandResult<this> {
|
||||
public parseKeywords(itemStream: IArgumentStream): ActionResult<this> {
|
||||
while (itemStream.peekItem() !== undefined && itemStream.peekItem() instanceof Keyword) {
|
||||
const item = itemStream.readItem() as Keyword;
|
||||
const description = this.description.description[item.designator];
|
||||
@@ -298,7 +298,7 @@ class KeywordParser {
|
||||
}
|
||||
} else {
|
||||
const associatedPropertyResult = this.readKeywordAssociatedProperty(description, itemStream);
|
||||
if (associatedPropertyResult.isErr()) {
|
||||
if (isError(associatedPropertyResult)) {
|
||||
return associatedPropertyResult;
|
||||
} else {
|
||||
this.arguments.set(description.name, associatedPropertyResult.ok);
|
||||
@@ -306,21 +306,21 @@ class KeywordParser {
|
||||
}
|
||||
|
||||
}
|
||||
return CommandResult.Ok(this);
|
||||
return Ok(this);
|
||||
}
|
||||
|
||||
public async parseRest(stream: IArgumentStream, shouldPromptForRest = false, restDescription?: RestDescription): Promise<CommandResult<ReadItem[]|undefined>> {
|
||||
public async parseRest(stream: IArgumentStream, shouldPromptForRest = false, restDescription?: RestDescription): Promise<ActionResult<ReadItem[]|undefined>> {
|
||||
if (restDescription !== undefined) {
|
||||
return await restDescription.parseRest(stream, shouldPromptForRest, this)
|
||||
} else {
|
||||
const result = this.parseKeywords(stream);
|
||||
if (result.isErr()) {
|
||||
return CommandResult.Err(result.err);
|
||||
if (isError(result)) {
|
||||
return result;
|
||||
}
|
||||
if (stream.peekItem() !== undefined) {
|
||||
return CommandError.Result(`There is an unexpected non-keyword argument ${JSON.stringify(stream.peekItem())}`);
|
||||
return ActionError.Result(`There is an unexpected non-keyword argument ${JSON.stringify(stream.peekItem())}`);
|
||||
} else {
|
||||
return CommandResult.Ok(undefined);
|
||||
return Ok(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,11 +347,11 @@ export interface ParameterDescription<ExecutorContext = unknown> {
|
||||
prompt?: Prompt<ExecutorContext>,
|
||||
}
|
||||
|
||||
export type ParameterParser = (stream: IArgumentStream) => Promise<CommandResult<ParsedArguments>>;
|
||||
export type ParameterParser = (stream: IArgumentStream) => Promise<ActionResult<ParsedArguments>>;
|
||||
|
||||
// So this should really just be something used by defineInterfaceCommand which turns parameters into a validator that can be used.
|
||||
// It can't be, because then otherwise how does the semantics for union work?
|
||||
// We should have a new type of CommandResult that accepts a ParamterDescription, and can render what's wrong (e.g. missing parameter).
|
||||
// We should have a new type of ActionResult that accepts a ParamterDescription, and can render what's wrong (e.g. missing parameter).
|
||||
// Showing where in the item stream it is missing and the command syntax and everything lovely like that.
|
||||
// How does that work with Union?
|
||||
export function parameters(descriptions: ParameterDescription[], rest: undefined|RestDescription = undefined, keywords: KeywordsDescription = new KeywordsDescription({}, false)): IArgumentListParser {
|
||||
@@ -377,21 +377,21 @@ class ArgumentListParser implements IArgumentListParser {
|
||||
) {
|
||||
}
|
||||
|
||||
public async parse(stream: IArgumentStream): Promise<CommandResult<ParsedArguments>> {
|
||||
public async parse(stream: IArgumentStream): Promise<ActionResult<ParsedArguments>> {
|
||||
let hasPrompted = false;
|
||||
const keywordsParser = this.keywords.getParser();
|
||||
for (const parameter of this.descriptions) {
|
||||
// it eats any keywords at any point in the stream
|
||||
// as they can appear at any point technically.
|
||||
const keywordResult = keywordsParser.parseKeywords(stream);
|
||||
if (keywordResult.isErr()) {
|
||||
return CommandResult.Err(keywordResult.err);
|
||||
if (isError(keywordResult)) {
|
||||
return keywordResult;
|
||||
}
|
||||
if (stream.peekItem() === undefined) {
|
||||
if (parameter.prompt && stream.isPromptable()) {
|
||||
const promptResult = await stream.prompt(parameter);
|
||||
if (promptResult.isErr()) {
|
||||
return promptResult as any;
|
||||
if (isError(promptResult)) {
|
||||
return promptResult;
|
||||
}
|
||||
hasPrompted = true;
|
||||
} else {
|
||||
@@ -402,20 +402,20 @@ class ArgumentListParser implements IArgumentListParser {
|
||||
}
|
||||
}
|
||||
const result = parameter.acceptor.validator(stream.peekItem());
|
||||
if (result.isErr()) {
|
||||
return ArgumentParseError.Result(result.err.message, { parameter, stream });
|
||||
if (isError(result)) {
|
||||
return ArgumentParseError.Result(result.error.message, { parameter, stream });
|
||||
}
|
||||
stream.readItem();
|
||||
}
|
||||
const restResult = await keywordsParser.parseRest(stream, hasPrompted, this.rest);
|
||||
if (restResult.isErr()) {
|
||||
return CommandResult.Err(restResult.err);
|
||||
if (isError(restResult)) {
|
||||
return restResult;
|
||||
}
|
||||
const immediateArguments = restResult.ok === undefined
|
||||
|| restResult.ok.length === 0
|
||||
? stream.source
|
||||
: stream.source.slice(0, stream.source.indexOf(restResult.ok[0]))
|
||||
return CommandResult.Ok({
|
||||
return Ok({
|
||||
immediateArguments: immediateArguments,
|
||||
keywords: keywordsParser.getKeywords(),
|
||||
rest: restResult.ok
|
||||
@@ -423,15 +423,15 @@ class ArgumentListParser implements IArgumentListParser {
|
||||
}
|
||||
}
|
||||
|
||||
export class AbstractArgumentParseError extends CommandError {
|
||||
export class AbstractArgumentParseError extends ActionError {
|
||||
constructor(
|
||||
public readonly stream: IArgumentStream,
|
||||
message: string) {
|
||||
super(message)
|
||||
}
|
||||
|
||||
public static Result<Ok>(message: string, options: { stream: IArgumentStream }): CommandResult<Ok, AbstractArgumentParseError> {
|
||||
return CommandResult.Err(new AbstractArgumentParseError(options.stream, message));
|
||||
public static Result(message: string, options: { stream: IArgumentStream }): ActionResult<never, AbstractArgumentParseError> {
|
||||
return ResultError(new AbstractArgumentParseError(options.stream, message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,14 +443,14 @@ export class ArgumentParseError extends AbstractArgumentParseError {
|
||||
super(stream, message)
|
||||
}
|
||||
|
||||
public static Result<Ok>(message: string, options: { parameter: ParameterDescription, stream: IArgumentStream }): CommandResult<Ok, ArgumentParseError> {
|
||||
return CommandResult.Err(new ArgumentParseError(options.parameter, options.stream, message));
|
||||
public static Result<Ok>(message: string, options: { parameter: ParameterDescription, stream: IArgumentStream }): ActionResult<Ok, ArgumentParseError> {
|
||||
return ResultError(new ArgumentParseError(options.parameter, options.stream, message));
|
||||
}
|
||||
}
|
||||
|
||||
export class UnexpectedArgumentError extends AbstractArgumentParseError {
|
||||
public static Result<Ok>(message: string, options: { stream: IArgumentStream }): CommandResult<Ok, UnexpectedArgumentError> {
|
||||
return CommandResult.Err(new UnexpectedArgumentError(options.stream, message));
|
||||
public static Result<Ok>(message: string, options: { stream: IArgumentStream }): ActionResult<Ok, UnexpectedArgumentError> {
|
||||
return ResultError(new UnexpectedArgumentError(options.stream, message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,10 +464,10 @@ export function union(...presentationTypes: PresentationType[]): PresentationTyp
|
||||
return {
|
||||
name,
|
||||
validator: (readItem: ReadItem) => {
|
||||
if (presentationTypes.some(p => p.validator(readItem).isOk())) {
|
||||
return CommandResult.Ok(true);
|
||||
if (presentationTypes.some(p => p.validator(readItem).isOkay)) {
|
||||
return Ok(true);
|
||||
} else {
|
||||
return CommandError.Result(`Read item didn't match any of the presentaiton types ${name}`);
|
||||
return ActionError.Result(`Read item didn't match any of the presentaiton types ${name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
import { ActionResult } from "matrix-protection-suite";
|
||||
import { ReadItem } from "./CommandReader";
|
||||
import { BaseFunction, InterfaceCommand } from "./InterfaceCommand";
|
||||
import { ArgumentStream, ParameterDescription } from "./ParameterParsing";
|
||||
import { CommandResult } from "./Validation";
|
||||
|
||||
export interface PromptOptions<PresentationType = any> {
|
||||
readonly suggestions: PresentationType[]
|
||||
@@ -19,7 +19,7 @@ export interface PromptOptions<PresentationType = any> {
|
||||
*/
|
||||
export interface InterfaceAcceptor<PresentationType = any> {
|
||||
readonly isPromptable: boolean
|
||||
promptForAccept(parameter: ParameterDescription, invocationRecord: CommandInvocationRecord): Promise<CommandResult<PresentationType>>
|
||||
promptForAccept(parameter: ParameterDescription, invocationRecord: CommandInvocationRecord): Promise<ActionResult<PresentationType>>
|
||||
}
|
||||
|
||||
export interface CommandInvocationRecord {
|
||||
@@ -43,12 +43,12 @@ export class PromptableArgumentStream extends ArgumentStream {
|
||||
return this.interfaceAcceptor.isPromptable
|
||||
}
|
||||
|
||||
public async prompt<T = ReadItem>(parameterDescription: ParameterDescription): Promise<CommandResult<T>> {
|
||||
public async prompt<T = ReadItem>(parameterDescription: ParameterDescription): Promise<ActionResult<T>> {
|
||||
const result = await this.interfaceAcceptor.promptForAccept(
|
||||
parameterDescription,
|
||||
this.invocationRecord
|
||||
);
|
||||
if (result.isOk()) {
|
||||
if (result.isOkay) {
|
||||
this.source.push(result.ok);
|
||||
}
|
||||
return result;
|
||||
|
||||
Reference in new issue
Block a user