Commit 7ab3dcc9 authored by nanahira's avatar nanahira

better If and For flow control

parent 1b06b896
......@@ -127,11 +127,13 @@ export const PluginName = (name: string) =>
export const If = <T>(
func: Condition<boolean, T, [Record<string, any>]>,
): TypedMethodDecorator<T> => Metadata.append('KoishiIf', func);
): TypedMethodDecorator<T> =>
Metadata.append('KoishiControl', { type: 'if', condition: func });
export const For = <T>(
func: Condition<Iterable<Record<string, any>>, T, [Record<string, any>]>,
): TypedMethodDecorator<T> => Metadata.append('KoishiFor', func);
): TypedMethodDecorator<T> =>
Metadata.append('KoishiControl', { type: 'for', condition: func });
export const UseModel = (...models: ModelClassType[]): ClassDecorator =>
TopLevelAction((ctx) => {
......
// metadatas
import { Context, Schema } from 'koishi';
import { Condition, ProvideDefinition, SystemInjectFun } from './interfaces';
import {
Condition,
ControlType,
ControlTypeMap,
ProvideDefinition,
SystemInjectFun,
} from './interfaces';
import { ClassType } from 'schemastery-gen';
export const KoishiServiceInjectSym = 'KoishiServiceInjectSym';
......@@ -19,12 +25,7 @@ export interface MetadataArrayMap {
KoishiSystemInjectSymKeys: string;
KoishiAddUsingList: keyof Context.Services;
KoishiPartialUsing: keyof Context.Services;
KoishiIf: Condition<boolean, any, [Record<string, any>]>;
KoishiFor: Condition<
Iterable<Record<string, any>>,
any,
[Record<string, any>]
>;
KoishiControl: ControlType;
}
export interface MetadataMap {
......
......@@ -53,3 +53,15 @@ export type TypedMethodDecorator<T> = <P>(
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<P>,
) => void;
export interface ControlTypeMap {
if: boolean;
for: Iterable<Record<string, any>>;
}
export interface ControlType<
T extends keyof ControlTypeMap = keyof ControlTypeMap,
> {
type: T;
condition: Condition<ControlTypeMap[T], any, [Record<string, any>]>;
}
import { Context, Plugin, Schema, WebSocketLayer } from 'koishi';
import {
Condition,
ControlType,
KoishiAddUsingList,
KoishiPartialUsing,
KoishiServiceInjectSym,
......@@ -150,8 +151,6 @@ export function DefinePlugin<T>(
methodKey: keyof C & string,
view: Record<string, any> = {},
) {
const conditions = reflector.getArray('KoishiIf', this, methodKey);
if (conditions.some((condition) => !condition(this, view))) return;
const ctx = this.__registrar.getScopeContext(
this.__ctx,
methodKey,
......@@ -178,27 +177,40 @@ export function DefinePlugin<T>(
_registerDeclarationsWithStack(
methodKey: keyof C & string,
stack: Condition<
Iterable<Record<string, any>>,
any,
[Record<string, any>]
>[],
stack: ControlType[],
existing: Record<string, any> = {},
) {
if (!stack.length) {
return this._registerDeclarationsResolving(methodKey, existing);
}
const [iter, ...rest] = stack;
for (const view of iter(this, existing)) {
this._registerDeclarationsWithStack(methodKey, rest, {
...existing,
...view,
});
const rest = [...stack];
const control = rest.pop();
switch (control.type) {
case 'if':
if (!(control as ControlType<'if'>).condition(this, existing))
return;
return this._registerDeclarationsWithStack(
methodKey,
rest,
existing,
);
case 'for':
for (const view of (control as ControlType<'for'>).condition(
this,
existing,
)) {
this._registerDeclarationsWithStack(methodKey, rest, {
...existing,
...view,
});
}
return;
}
}
_registerDeclarationsFor(methodKey: keyof C & string) {
const stack = reflector.getArray('KoishiFor', this, methodKey);
const stack = reflector.getArray('KoishiControl', this, methodKey);
return this._registerDeclarationsWithStack(methodKey, stack);
}
......
......@@ -23,8 +23,9 @@ class MyPlugin extends BasePlugin<{ foo: boolean; bar: boolean }> {
class MyPlugin2 extends BasePlugin<{
prefix: string;
commands: { name: string; return: string }[];
matrix: { commands: { name: string; return: string }[] }[];
}> {
@For(({ config }) => config.commands)
@For<MyPlugin2>(({ config }) => config.commands)
@If<MyPlugin2>((_, def) => def.name !== 'badthing')
@UseCommand('{{name}}')
onCommand(
......@@ -33,6 +34,16 @@ class MyPlugin2 extends BasePlugin<{
) {
return prefix + returnValue;
}
@For<MyPlugin2>(({ config }) => config.matrix)
@For<MyPlugin2>((_, matrix) => matrix.commands)
@If<MyPlugin2>((_, def) => def.name !== 'badthing')
@UseCommand('{{name}}')
onMatrix(
@PutValue('{{return}}') returnValue: string,
@PutValue('{{prefix}}') prefix: string,
) {
return prefix + returnValue;
}
}
describe('It should register conditionally', () => {
......@@ -54,6 +65,22 @@ describe('It should register conditionally', () => {
{ name: 'bar', return: 'baz' },
{ name: 'badthing', return: 'bad' },
],
matrix: [
{
commands: [
{ name: 'foo1', return: 'bar1' },
{ name: 'bar1', return: 'baz1' },
{ name: 'badthing', return: 'bad' },
],
},
{
commands: [
{ name: 'foo2', return: 'bar2' },
{ name: 'bar2', return: 'baz2' },
{ name: 'badthing', return: 'bad' },
],
},
],
prefix: '> ',
});
await app.start();
......@@ -61,6 +88,14 @@ describe('It should register conditionally', () => {
const commandBar = app.command('bar');
expect(await commandFoo.execute({})).toBe('> bar');
expect(await commandBar.execute({})).toBe('> baz');
const commandFoo1 = app.command('foo1');
const commandBar1 = app.command('bar1');
expect(await commandFoo1.execute({})).toBe('> bar1');
expect(await commandBar1.execute({})).toBe('> baz1');
const commandFoo2 = app.command('foo2');
const commandBar2 = app.command('bar2');
expect(await commandFoo2.execute({})).toBe('> bar2');
expect(await commandBar2.execute({})).toBe('> baz2');
expect(await app.command('badthing').execute({})).toBeFalsy();
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment