diff --git a/__tests__/InvalidMutations.ts b/__tests__/InvalidMutations.ts index 17b62f6bf..2345eb393 100644 --- a/__tests__/InvalidMutations.ts +++ b/__tests__/InvalidMutations.ts @@ -40,23 +40,23 @@ class R1 extends Reactor { function (this, __in1, __in2, __out1, __out2) { test("expect error on creating creating direct feed through", () => { expect(() => { - this.connect(__in2, __out2); + this.connect(__in2.asConnectable(), __out2.asConnectable()); }).toThrowError("New connection introduces direct feed through."); }); test("expect error when creating connection outside container", () => { expect(() => { - this.connect(__out2, __in2); + this.connect(__out2.asConnectable(), __in2.asConnectable()); }).toThrowError("New connection is outside of container."); }); const R2 = new R1(this.getReactor()); test("expect error on mutation creating race condition on an output port", () => { expect(() => { - this.connect(R2.out1, __out1); + this.connect(R2.out1.asConnectable(), __out1.asConnectable()); }).toThrowError("Destination port is already occupied."); }); test("expect error on spawning and creating loop within a reactor", () => { expect(() => { - this.connect(R2.out1, R2.in1); + this.connect(R2.out1.asConnectable(), R2.in1.asConnectable()); }).toThrowError("New connection introduces cycle."); }); } diff --git a/__tests__/SimpleMutation.test.ts b/__tests__/SimpleMutation.test.ts index e85d06766..bd1345584 100644 --- a/__tests__/SimpleMutation.test.ts +++ b/__tests__/SimpleMutation.test.ts @@ -46,7 +46,7 @@ class R2 extends Reactor { function (this, __in, __out) { test("expect error to be thrown", () => { expect(() => { - this.connect(__out, __in); + this.connect(__out.asConnectable(), __in.asConnectable()); }).toThrowError("New connection is outside of container."); }); } diff --git a/__tests__/disconnect.test.ts b/__tests__/disconnect.test.ts index b25b31cb2..c876c3365 100644 --- a/__tests__/disconnect.test.ts +++ b/__tests__/disconnect.test.ts @@ -54,11 +54,11 @@ class R1 extends Reactor { const R2 = new R1(this.getReactor()); test("expect that disconnecting an existing connection will not result in an error being thrown", () => { expect(() => { - this.connect(R2.out2, R2.in2); + this.connect(R2.out2.asConnectable(), R2.in2.asConnectable()); this.disconnect(R2.out2, R2.in2); - this.connect(R2.out2, R2.in2); + this.connect(R2.out2.asConnectable(), R2.in2.asConnectable()); this.disconnect(R2.out2); - this.connect(R2.out2, R2.in2); + this.connect(R2.out2.asConnectable(), R2.in2.asConnectable()); }).not.toThrow(); }); } diff --git a/__tests__/mutations.test.ts b/__tests__/mutations.test.ts index 090e90053..55c949642 100644 --- a/__tests__/mutations.test.ts +++ b/__tests__/mutations.test.ts @@ -92,7 +92,7 @@ class Computer extends Reactor { continue; } const x = new AddOne(this.getReactor(), id); - this.connect(src, x.input); + this.connect(src.asConnectable(), x.input.asConnectable()); } } }); diff --git a/src/core/port.ts b/src/core/port.ts index e6206e821..8870d1cd0 100644 --- a/src/core/port.ts +++ b/src/core/port.ts @@ -7,7 +7,8 @@ import type { Absent, MultiReadWrite, ReadWrite, - Variable + Variable, + Read } from "./internal"; import {Trigger, Log} from "./internal"; @@ -59,6 +60,13 @@ export abstract class Port extends Trigger { } } +export class ConnectablePort implements Read { + public get = (): Absent => undefined; + public getPort = (): IOPort => this.port; + + constructor(public port: IOPort) {} +} + /** * Abstract class for a writable port. It is intended as a wrapper for a * regular port. In addition to a get method, it also has a set method and @@ -103,6 +111,10 @@ export abstract class IOPort extends Port { } } + public asConnectable(): ConnectablePort { + return new ConnectablePort(this); + } + /** * Only the holder of the key may obtain a writable port. * @param key diff --git a/src/core/reactor.ts b/src/core/reactor.ts index 603a568e9..7fa4263cc 100644 --- a/src/core/reactor.ts +++ b/src/core/reactor.ts @@ -42,7 +42,8 @@ import { Startup, Shutdown, WritableMultiPort, - Dummy + Dummy, + ConnectablePort } from "./internal"; import {v4 as uuidv4} from "uuid"; import {Bank} from "./bank"; @@ -440,16 +441,31 @@ export abstract class Reactor extends Component { * @param src * @param dst */ + + public connect( + src: ConnectablePort, + dst: ConnectablePort + ): void; + public connect( + src: CallerPort, + dst: CalleePort + ): void; public connect( - src: CallerPort | IOPort, - dst: CalleePort | IOPort + ...[src, dst]: + | [ConnectablePort, ConnectablePort] + | [CallerPort, CalleePort] ): void { if (src instanceof CallerPort && dst instanceof CalleePort) { this.reactor._connectCall(src, dst); - } else if (src instanceof IOPort && dst instanceof IOPort) { - this.reactor._connect(src, dst); + } else if ( + src instanceof ConnectablePort && + dst instanceof ConnectablePort + ) { + this.reactor._connect(src.getPort(), dst.getPort()); } else { - // ERROR + throw Error( + "Logically unreachable code: src and dst type mismatch, Caller(ee) port cannot be connected to IOPort." + ); } } @@ -1804,10 +1820,13 @@ interface UtilityFunctions { } export interface MutationSandbox extends ReactionSandbox { - connect: ( - src: CallerPort | IOPort, - dst: CalleePort | IOPort - ) => void; + connect: { + (src: ConnectablePort, dst: ConnectablePort): void; + ( + src: CallerPort, + dst: CalleePort + ): void; + }; disconnect: (src: IOPort, dst?: IOPort) => void;