|
| 1 | +import { ReadWriteConnection } from "../common/connection"; |
| 2 | +import { NewEvalMessage, ServerMessage, EvalDoneMessage, EvalFailedMessage, TypedValue, ClientMessage } from "../proto"; |
| 3 | +import { Emitter } from "@coder/events"; |
| 4 | +import { logger, field } from "@coder/logger"; |
| 5 | + |
| 6 | + |
| 7 | +export class Client { |
| 8 | + |
| 9 | + private evalId: number = 0; |
| 10 | + private evalDoneEmitter: Emitter<EvalDoneMessage> = new Emitter(); |
| 11 | + private evalFailedEmitter: Emitter<EvalFailedMessage> = new Emitter(); |
| 12 | + |
| 13 | + public constructor( |
| 14 | + private readonly connection: ReadWriteConnection, |
| 15 | + ) { |
| 16 | + connection.onMessage((data) => { |
| 17 | + try { |
| 18 | + this.handleMessage(ServerMessage.deserializeBinary(data)); |
| 19 | + } catch (ex) { |
| 20 | + logger.error("Failed to handle server message", field("length", data.byteLength), field("exception", ex)); |
| 21 | + } |
| 22 | + }); |
| 23 | + } |
| 24 | + |
| 25 | + public evaluate<R>(func: () => R): Promise<R>; |
| 26 | + public evaluate<R, T1>(func: (a1: T1) => R, a1: T1): Promise<R>; |
| 27 | + public evaluate<R, T1, T2>(func: (a1: T1, a2: T2) => R, a1: T1, a2: T2): Promise<R>; |
| 28 | + public evaluate<R, T1, T2, T3>(func: (a1: T1, a2: T2, a3: T3) => R, a1: T1, a2: T2, a3: T3): Promise<R>; |
| 29 | + public evaluate<R, T1, T2, T3, T4>(func: (a1: T1, a2: T2, a3: T3, a4: T4) => R, a1: T1, a2: T2, a3: T3, a4: T4): Promise<R>; |
| 30 | + public evaluate<R, T1, T2, T3, T4, T5>(func: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5) => R, a1: T1, a2: T2, a3: T3, a4: T4, a5: T5): Promise<R>; |
| 31 | + public evaluate<R, T1, T2, T3, T4, T5, T6>(func: (a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6) => R, a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6): Promise<R>; |
| 32 | + public evaluate<R, T1, T2, T3, T4, T5, T6>(func: (a1?: T1, a2?: T2, a3?: T3, a4?: T4, a5?: T5, a6?: T6) => R, a1?: T1, a2?: T2, a3?: T3, a4?: T4, a5?: T5, a6?: T6): Promise<R> { |
| 33 | + const newEval = new NewEvalMessage(); |
| 34 | + const id = this.evalId++; |
| 35 | + newEval.setId(id); |
| 36 | + newEval.setArgsList([a1, a2, a3, a4, a5, a6].filter(a => a).map(a => JSON.stringify(a))); |
| 37 | + newEval.setFunction(func.toString()); |
| 38 | + |
| 39 | + const clientMsg = new ClientMessage(); |
| 40 | + clientMsg.setNewEval(newEval); |
| 41 | + this.connection.send(clientMsg.serializeBinary()); |
| 42 | + |
| 43 | + let res: (value?: R) => void; |
| 44 | + let rej: (err?: any) => void; |
| 45 | + const prom = new Promise<R>((r, e) => { |
| 46 | + res = r; |
| 47 | + rej = e; |
| 48 | + }); |
| 49 | + |
| 50 | + const d1 = this.evalDoneEmitter.event((doneMsg) => { |
| 51 | + if (doneMsg.getId() === id) { |
| 52 | + d1.dispose(); |
| 53 | + d2.dispose(); |
| 54 | + |
| 55 | + const resp = doneMsg.getResponse(); |
| 56 | + if (!resp) { |
| 57 | + res(); |
| 58 | + |
| 59 | + return; |
| 60 | + } |
| 61 | + |
| 62 | + const rt = resp.getType(); |
| 63 | + let val: any; |
| 64 | + switch (rt) { |
| 65 | + case TypedValue.Type.BOOLEAN: |
| 66 | + val = resp.getValue() === "true"; |
| 67 | + break; |
| 68 | + case TypedValue.Type.NUMBER: |
| 69 | + val = parseInt(resp.getValue(), 10); |
| 70 | + break; |
| 71 | + case TypedValue.Type.OBJECT: |
| 72 | + val = JSON.parse(resp.getValue()); |
| 73 | + break; |
| 74 | + case TypedValue.Type.STRING: |
| 75 | + val = resp.getValue(); |
| 76 | + break; |
| 77 | + default: |
| 78 | + throw new Error(`unsupported typed value ${rt}`); |
| 79 | + } |
| 80 | + |
| 81 | + res(val); |
| 82 | + } |
| 83 | + }); |
| 84 | + |
| 85 | + const d2 = this.evalFailedEmitter.event((failedMsg) => { |
| 86 | + if (failedMsg.getId() === id) { |
| 87 | + d1.dispose(); |
| 88 | + d2.dispose(); |
| 89 | + |
| 90 | + rej(failedMsg.getMessage()); |
| 91 | + } |
| 92 | + }); |
| 93 | + |
| 94 | + return prom; |
| 95 | + } |
| 96 | + |
| 97 | + private handleMessage(message: ServerMessage): void { |
| 98 | + if (message.hasEvalDone()) { |
| 99 | + this.evalDoneEmitter.emit(message.getEvalDone()!); |
| 100 | + } else if (message.hasEvalFailed()) { |
| 101 | + this.evalFailedEmitter.emit(message.getEvalFailed()!); |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | +} |
0 commit comments