From e5f5590a67860113bcb7faf8c88f2df834cb728b Mon Sep 17 00:00:00 2001 From: wancat Date: Thu, 7 Mar 2024 00:42:49 +0800 Subject: [PATCH] feat: add "or" and "not" matchers --- packages/game/src/condition/condition.ts | 12 +++++++++++- packages/game/src/condition/notMatcher.ts | 10 ++++++++++ packages/game/src/condition/orMatcher.ts | 10 ++++++++++ packages/game/test/condition.test.ts | 18 ++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 packages/game/src/condition/notMatcher.ts create mode 100644 packages/game/src/condition/orMatcher.ts diff --git a/packages/game/src/condition/condition.ts b/packages/game/src/condition/condition.ts index f65d9b8..6188ef3 100644 --- a/packages/game/src/condition/condition.ts +++ b/packages/game/src/condition/condition.ts @@ -1,24 +1,34 @@ +/* eslint-disable max-classes-per-file */ import { Command } from '../command.js' import { PwdMatcher } from './pwdMatcher.js' import { CommandMatcher } from './commandMatcher.js' import { ErrorMatcher } from './errorMatcher.js' import { Matcher } from './matcher.js' import { OutputMatcher } from './outputMatcher.js' +import { OrMatcher } from './orMatcher.js' +import { NotMatcher } from './notMatcher.js' export interface ICondition { command?: string output?: string error?: string pwd?: string + or?: ICondition[] + not?: ICondition } export class Condition { private matchers: Matcher[] = [] - constructor({ command, output, error, pwd }: ICondition) { + constructor({ command, output, error, pwd, or, not }: ICondition) { if (command) this.matchers.push(new CommandMatcher(command)) if (output) this.matchers.push(new OutputMatcher(output)) if (pwd) this.matchers.push(new PwdMatcher(pwd)) + if (or) { + const conditions = or.map((condition) => new Condition(condition)) + this.matchers.push(new OrMatcher(conditions)) + } + if (not) this.matchers.push(new NotMatcher(new Condition(not))) this.matchers.push(new ErrorMatcher(error)) // always check unexpected error } diff --git a/packages/game/src/condition/notMatcher.ts b/packages/game/src/condition/notMatcher.ts new file mode 100644 index 0000000..ac3ae99 --- /dev/null +++ b/packages/game/src/condition/notMatcher.ts @@ -0,0 +1,10 @@ +import { Command } from '../command.js' +import { Matcher } from './matcher.js' + +export class NotMatcher implements Matcher { + constructor(private condition: Matcher) {} + + match(command: Partial): boolean { + return !this.condition.match(command) + } +} diff --git a/packages/game/src/condition/orMatcher.ts b/packages/game/src/condition/orMatcher.ts new file mode 100644 index 0000000..1703c6c --- /dev/null +++ b/packages/game/src/condition/orMatcher.ts @@ -0,0 +1,10 @@ +import { Command } from '../command.js' +import { Matcher } from './matcher.js' + +export class OrMatcher implements Matcher { + constructor(private conditions: Matcher[]) {} + + match(command: Partial): boolean { + return this.conditions.some((condition) => condition.match(command)) + } +} diff --git a/packages/game/test/condition.test.ts b/packages/game/test/condition.test.ts index 0310331..554a0ab 100644 --- a/packages/game/test/condition.test.ts +++ b/packages/game/test/condition.test.ts @@ -62,3 +62,21 @@ test('condition match command', () => { condition.match({ command: 'echo start', error: 'permission denied' }) ).toBe(false) }) + +test('condition with OR', () => { + const condition = new Condition({ + or: [{ command: '^echo start$' }, { command: '^echo hello$' }], + }) + expect(condition.match({ command: 'echo start' })).toBe(true) + expect(condition.match({ command: 'echo hello' })).toBe(true) + expect(condition.match({ command: 'echo start1' })).toBe(false) +}) + +test('condition with NOT', () => { + const condition = new Condition({ + not: { command: '^echo start$' }, + }) + + expect(condition.match({ command: 'echo start' })).toBe(false) + expect(condition.match({ command: 'echo hello' })).toBe(true) +})