refactor: Create a new super class Event and prevent duplicate logic

This commit is contained in:
Justin Lin committed 2025-04-21 12:23:09 +08:00
1 parent 2dc40a2254
commit 0f0f43b838
6 files changed
+109 -49

No files matched your search

+48
View File
@@ -0,0 +1,48 @@
import { Condition } from './condition/Condition'
import {
ICommand,
ICondition,
IFileExistenceChecker,
IRequirements,
IResponse,
} from './schema'
export class Event {
id: string
private condition: Condition
private requirements: string[]
constructor(
id: string,
condition: ICondition,
private response: IResponse,
requirements?: IRequirements
) {
this.id = id
this.condition = new Condition(condition)
this.requirements = requirements || []
}
async satisfies(
command: ICommand,
checker: IFileExistenceChecker
): Promise<boolean> {
return this.condition.satisfies(command, checker)
}
private checkRequirements(completed: string[]): boolean {
return this.requirements.every((req) => completed.includes(req))
}
private finished(completed: string[]): boolean {
return completed.includes(this.id)
}
active(completed: string[]): boolean {
return !this.finished(completed) && this.checkRequirements(completed)
}
getResponse(): IResponse {
return this.response
}
}
+18
View File
@@ -0,0 +1,18 @@
/* eslint-disable max-classes-per-file */
import { Event } from './Event'
import { IStageException, IGlobalException } from './schema/exception'
export class StageException extends Event {
readonly catchAll: boolean
constructor({ id, condition, response, catchAll }: IStageException) {
super(id, condition, response)
this.catchAll = catchAll
}
}
export class GlobalException extends Event {
constructor({ id, condition, response, requirements }: IGlobalException) {
super(id, condition, response, requirements)
}
}
+21 -16
View File
@@ -1,5 +1,7 @@
/* eslint-disable no-await-in-loop */
import { Condition } from './condition/Condition'
import { Event } from './Event'
import { GlobalException, StageException } from './Exception'
import {
IQuest,
IStage,
@@ -10,15 +12,28 @@ import {
import { Stage } from './Stage'
export class Quest {
private stages: Stage[]
private quest: IQuest
private stages: Stage[]
private exceptions: GlobalException[] = []
private events: Map<string, Event> = new Map()
constructor(
quest: IQuest,
private checker: IFileExistenceChecker
) {
this.quest = quest
this.stages = quest.stages.map((stage: IStage) => new Stage(stage))
this.stages = quest.stages.map((stage) => new Stage(stage))
for (const stage of this.stages) {
this.events.set(stage.id, stage)
for (const exception of stage.getExceptions()) {
this.events.set(exception.id, exception)
}
}
for (const exception of quest.exceptions ?? []) {
const globalException = new GlobalException(exception)
this.exceptions.push(globalException)
this.events.set(globalException.id, globalException)
}
}
getActiveStages(completed: string[]) {
@@ -26,15 +41,7 @@ export class Quest {
}
private getActiveExceptions(completed: string[]) {
return (
this.quest.exceptions?.filter(
(exception) =>
!completed.includes(exception.id) &&
exception.requirements.every((requirement) =>
completed.includes(requirement)
)
) ?? []
)
return this.exceptions.filter((exception) => exception.active(completed))
}
async findSatisfiedEvent(
@@ -49,17 +56,15 @@ export class Quest {
return stage.id
}
}
for (const exception of stages.flatMap((stage) => stage.exceptions)) {
const condition = new Condition(exception.condition)
for (const exception of stages.flatMap((stage) => stage.getExceptions())) {
if (exception.catchAll && !catchAllException) {
catchAllException = exception.id
} else if (await condition.satisfies(command, this.checker)) {
} else if (await exception.satisfies(command, this.checker)) {
return exception.id
}
}
for (const exception of this.getActiveExceptions(completed)) {
const condition = new Condition(exception.condition)
if (await condition.satisfies(command, this.checker)) {
if (await exception.satisfies(command, this.checker)) {
return exception.id
}
}
+18 -30
View File
@@ -1,38 +1,26 @@
import { IStage, ICommand, IFileExistenceChecker } from './schema'
import { Condition } from './condition/Condition'
import { StageException } from './schema/exception'
import { IStage } from './schema'
import { StageException } from './Exception'
import { Event } from './Event'
export class Stage {
id: string
export class Stage extends Event {
name: string
public readonly condition: Condition
private requirements: string[]
public readonly exceptions: StageException[]
private exceptions: StageException[]
constructor({ id, name, condition, requirements, exceptions }: IStage) {
this.id = id
constructor({
id,
name,
condition,
requirements,
exceptions,
response,
}: IStage) {
super(id, condition, response, requirements)
this.name = name || ''
this.condition = new Condition(condition)
this.requirements = requirements || []
this.exceptions = exceptions || []
this.exceptions =
exceptions?.map((exception) => new StageException(exception)) || []
}
async satisfies(
command: ICommand,
checker: IFileExistenceChecker
): Promise<boolean> {
return this.condition.satisfies(command, checker)
}
private checkRequirements(completed: string[]): boolean {
return this.requirements.every((req) => completed.includes(req))
}
private finished(completed: string[]): boolean {
return completed.includes(this.id)
}
active(completed: string[]): boolean {
return !this.finished(completed) && this.checkRequirements(completed)
getExceptions(): StageException[] {
return this.exceptions
}
}
+3 -3
View File
@@ -9,16 +9,16 @@ export const exceptionSchema = z.object({
response: responseSchema,
})
export type Exception = z.infer<typeof exceptionSchema>
export type IException = z.infer<typeof exceptionSchema>
export const stageExceptionSchema = exceptionSchema.extend({
catchAll: z.boolean().default(false),
})
export type StageException = z.infer<typeof stageExceptionSchema>
export type IStageException = z.infer<typeof stageExceptionSchema>
export const globalExceptionSchema = exceptionSchema.extend({
requirements: requirementsSchema.default([]),
})
export type GlobalException = z.infer<typeof globalExceptionSchema>
export type IGlobalException = z.infer<typeof globalExceptionSchema>
+1
View File
@@ -3,6 +3,7 @@ export { stageSchema, IStage } from './stage.js'
export { requirementsSchema, IRequirements } from './requirements.js'
export { responseSchema, IResponse } from './response.js'
export { questSchema, IQuest } from './quest.js'
export { exceptionSchema, IException } from './exception.js'
export interface ICommand {
command?: string