Commit 11ecc338 authored by nanahira's avatar nanahira

add PutObject for class-based command params

parent f95469e6
...@@ -221,6 +221,7 @@ export const PutTemplate = (name: string, text: string | Dict<string>) => ...@@ -221,6 +221,7 @@ export const PutTemplate = (name: string, text: string | Dict<string>) =>
name, name,
text: adaptLocaleDict(text), text: adaptLocaleDict(text),
}); });
export const PutObject = () => CommandPut.decorate('typeClass');
export const TopLevelAction = (action: TopLevelActionDef): ClassDecorator => export const TopLevelAction = (action: TopLevelActionDef): ClassDecorator =>
Metadata.append('KoishiTopLevelAction', action); Metadata.append('KoishiTopLevelAction', action);
......
// metadatas // metadatas
import { import {
CommandDefinitionFun, CommandDefinitionFun,
MappingStruct,
OnContextFunction, OnContextFunction,
TopLevelActionDef, TopLevelActionDef,
} from './interfaces'; } from './interfaces';
import { DoRegister } from '../registry'; import { CommandPut, DoRegister } from '../registry';
export const KoishiOnContextScope = 'KoishiOnContextScope'; export const KoishiOnContextScope = 'KoishiOnContextScope';
export const KoishiDoRegister = 'KoishiDoRegister'; export const KoishiDoRegister = 'KoishiDoRegister';
...@@ -19,8 +20,13 @@ export interface MetadataArrayMap { ...@@ -19,8 +20,13 @@ export interface MetadataArrayMap {
KoishiCommandDefinition: CommandDefinitionFun; KoishiCommandDefinition: CommandDefinitionFun;
KoishiDoRegisterKeys: string; KoishiDoRegisterKeys: string;
KoishiTopLevelAction: TopLevelActionDef; KoishiTopLevelAction: TopLevelActionDef;
KoishiPutClassFieldKeys: string;
} }
export interface MetadataMap { export interface MetadataMap {
KoishiDoRegister: DoRegister.Config; KoishiDoRegister: DoRegister.Config;
KoishiPutClassField: MappingStruct<
CommandPut.ConfigMap,
keyof CommandPut.ConfigMap
>;
} }
...@@ -13,6 +13,8 @@ import { ...@@ -13,6 +13,8 @@ import {
applyOptionToCommand, applyOptionToCommand,
registerTemplate, registerTemplate,
} from '../../utility'; } from '../../utility';
import { Metadata } from '../../meta/metadata.decorators';
import { reflector } from '../../meta/meta-fetch';
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
export namespace CommandPut { export namespace CommandPut {
...@@ -29,6 +31,7 @@ export namespace CommandPut { ...@@ -29,6 +31,7 @@ export namespace CommandPut {
sessionField: keyof Session; sessionField: keyof Session;
renderer: string | undefined; renderer: string | undefined;
template: TemplateConfig; template: TemplateConfig;
typeClass: void;
} }
export type Config<K extends keyof ConfigMap = keyof ConfigMap> = export type Config<K extends keyof ConfigMap = keyof ConfigMap> =
...@@ -79,6 +82,20 @@ export namespace CommandPut { ...@@ -79,6 +82,20 @@ export namespace CommandPut {
registerTemplate(data, ctx, cmd), registerTemplate(data, ctx, cmd),
); );
preRegistry.extend('typeClass', (data, cmd, ctx, nativeType) => {
const keys = reflector.getArray('KoishiPutClassFieldKeys', nativeType);
for (const key of keys) {
const meta = reflector.get('KoishiPutClassField', nativeType, key);
if (!meta) continue;
const propertyNativeType = Reflect.getMetadata(
'design:type',
nativeType.prototype,
key,
);
preRegistry.execute(meta, cmd, ctx, propertyNativeType);
}
});
export const registry = new MethodRegistry< export const registry = new MethodRegistry<
ConfigMap, ConfigMap,
any, any,
...@@ -122,17 +139,47 @@ export namespace CommandPut { ...@@ -122,17 +139,47 @@ export namespace CommandPut {
(data, argv, args) => (params: object) => (data, argv, args) => (params: object) =>
argv.session.text(`.${data.name}`, params), argv.session.text(`.${data.name}`, params),
); );
registry.extend(
'typeClass',
(data, argv, args, nativeType: { new (): any }) => {
const keys = reflector.getArray('KoishiPutClassFieldKeys', nativeType);
const obj = new nativeType();
for (const key of keys) {
const meta = reflector.get('KoishiPutClassField', nativeType, key);
if (!meta) continue;
const propertyNativeType = Reflect.getMetadata(
'design:type',
nativeType.prototype,
key,
);
obj[key] = registry.execute(meta, argv, args, propertyNativeType);
}
return obj;
},
);
export function decorate<T extends keyof ConfigMap>( export function decorate<T extends keyof ConfigMap>(
type: T, type: T,
data?: ConfigMap[T], data?: ConfigMap[T],
): ParameterDecorator { ): ParameterDecorator & PropertyDecorator {
return (obj, key: string, index) => { return (obj, key: string, index?: number) => {
const objClass = obj.constructor; const def = GenerateMappingStruct(type, data);
const list: Config<T>[] = if (typeof index === 'number') {
Reflect.getMetadata(KoishiCommandPutDef, objClass, key) || []; // As a parameter decorator
list[index] = GenerateMappingStruct(type, data); const objClass = obj.constructor;
Reflect.defineMetadata(KoishiCommandPutDef, list, objClass, key); const list: Config<T>[] =
Reflect.getMetadata(KoishiCommandPutDef, objClass, key) || [];
list[index] = def;
Reflect.defineMetadata(KoishiCommandPutDef, list, objClass, key);
} else {
// As a property decorator
Metadata.set(
'KoishiPutClassField',
def,
'KoishiPutClassFieldKeys',
)(obj, key);
}
return obj;
}; };
} }
} }
import { import {
CommandUsage, CommandUsage,
PutArg, PutArg,
PutObject,
PutOption, PutOption,
UseCommand, UseCommand,
UseEvent, UseEvent,
...@@ -10,6 +11,14 @@ import { App, Command, Next, Session } from 'koishi'; ...@@ -10,6 +11,14 @@ import { App, Command, Next, Session } from 'koishi';
import { Registrar } from '../src/register'; import { Registrar } from '../src/register';
import { EventNameAndPrepend } from '../src/def'; import { EventNameAndPrepend } from '../src/def';
class SkirtArg {
@PutArg(0)
count: number;
@PutOption('color', '-c <color>')
color: string;
}
class MyClass { class MyClass {
@UseMiddleware() @UseMiddleware()
async onPing(session: Session, next: Next) { async onPing(session: Session, next: Next) {
...@@ -36,6 +45,11 @@ class MyClass { ...@@ -36,6 +45,11 @@ class MyClass {
async onCount(@PutArg(0) count: number) { async onCount(@PutArg(0) count: number) {
return `I have ${count} dresses.`; return `I have ${count} dresses.`;
} }
@UseCommand('skirt')
async onSkirt(@PutObject() arg: SkirtArg) {
return `I have ${arg.count} ${arg.color} skirts.`;
}
} }
const registrar = new Registrar(new MyClass()); const registrar = new Registrar(new MyClass());
...@@ -79,4 +93,15 @@ describe('Register', () => { ...@@ -79,4 +93,15 @@ describe('Register', () => {
expect(command._arguments[0].type).toBe('number'); expect(command._arguments[0].type).toBe('number');
expect(command.execute({ args: ['4'] })).resolves.toBe('I have 4 dresses.'); expect(command.execute({ args: ['4'] })).resolves.toBe('I have 4 dresses.');
}); });
it('should work on class type', () => {
const result = registrar.register(app, 'onSkirt');
expect(result.type).toBe('command');
const command: Command = result.result;
expect(command._arguments[0].type).toBe('number');
expect(command._options.color.type).toBe('string');
expect(
command.execute({ args: ['4'], options: { color: 'red' } }),
).resolves.toBe('I have 4 red skirts.');
});
}); });
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