webstudio/server/src/core/services/stack_service.ts
2023-11-10 12:06:40 +03:00

141 lines
3.7 KiB
TypeScript

import {
FilesChangeNotifierService,
IHashesCache,
} from "./files_change_notifier_service";
import { IPipeline } from "../model/process_model";
import { ExecutorProgramService } from "./executor_program_service";
import { EXEC_EVENT, ExecError, SpawnError } from "../model/exec_error_model";
import { TypedEvent } from "../helper/typed_event";
import { Result } from "../helper/result";
import { ExecutorResult } from "../model/executor_result";
import { delay } from "../helper/delay";
import { TriggerService } from "./trigger_service";
import { Trigger } from "../../features/triggers/trigger_model";
export interface Iteration {
hashes: IHashesCache | null;
process: IPipeline;
result?: ExecError | SpawnError | ExecutorResult;
}
export abstract class IStackService {
abstract callStack: Iteration[];
abstract path: string;
abstract init(processed: IPipeline[], path: string): void;
}
export class StackService
extends TypedEvent<Iteration[]>
implements IStackService
{
callStack: Iteration[];
path: string;
constructor(processed: IPipeline[], path: string) {
super();
this.path = path;
this.callStack = [];
this.init(processed);
}
public init(processed: IPipeline[]) {
for (let el of processed) {
el = this.commandHandler(el);
this.callStack.push({
hashes: null,
process: el,
});
}
}
private commandHandler(processMetaData: IPipeline) {
processMetaData.process.command = processMetaData.process.command.replace(
"$PATH",
this.path
);
return processMetaData;
}
public async call() {
let inc = 0;
for await (const el of this.callStack!) {
await this.execStack(inc, el);
inc += 1;
this.emit(this.callStack);
}
}
async execStack(
stackNumber: number,
stackLayer: Iteration
): Promise<void | boolean> {
const executorService = new ExecutorProgramService(this.path);
executorService.call(
stackLayer.process.process.type,
stackLayer.process.process.command
);
const filesChangeNotifierService = new FilesChangeNotifierService(
this.path
);
filesChangeNotifierService.call();
const result = await this.waitEvent<
Result<ExecError | SpawnError, ExecutorResult>
>(executorService);
await delay(100);
if (result.isSuccess()) {
this.callStack[stackNumber].result = result.value;
this.callStack[stackNumber].hashes = filesChangeNotifierService.hashes;
const triggerResult = await this.triggerExec(
stackLayer.process.trigger,
stackNumber
);
triggerResult.fold(
(s) => {
s;
},
(e) => {
e;
}
);
}
filesChangeNotifierService.cancel();
return;
}
public waitEvent<T>(stream: TypedEvent<T>): Promise<T> {
const promise = new Promise<T>((resolve, reject) => {
const addListener = () => {
stream.on((e) => {
const event = e as Result<ExecError | SpawnError, ExecutorResult>;
event.fold(
(s) => {
if (s.event === EXEC_EVENT.END) {
resolve(e);
}
},
(e) => {
reject(e);
}
);
});
};
addListener();
});
return promise;
}
private async triggerExec(
trigger: Trigger | null,
stackNumber: number
): Promise<Result<boolean, boolean>> {
if (trigger !== null) {
const hashes = this.callStack[stackNumber].hashes;
if (hashes != null) {
return await new TriggerService(trigger, hashes, this.path).call();
}
throw new Error("Hashes is null");
}
return Result.ok();
}
}