Skip to content

Commit f6e2e26

Browse files
committed
Refactor transformers to support both directions
1 parent 6d60eea commit f6e2e26

7 files changed

+113
-77
lines changed

lib/src/compiler/async.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {compilerCommand} from '../compiler-path';
2020
import {activeDeprecationOptions} from '../deprecations';
2121
import {FunctionRegistry} from '../function-registry';
2222
import {ImporterRegistry} from '../importer-registry';
23-
import {MessageTransformer} from '../message-transformer';
23+
import {HostMessageTransformer as MessageTransformer} from '../message-transformer';
2424
import {PacketTransformer} from '../packet-transformer';
2525
import * as utils from '../utils';
2626
import * as proto from '../vendor/embedded_sass_pb';
@@ -156,8 +156,8 @@ export class AsyncCompiler {
156156
this.writeStdin(buffer);
157157
});
158158
this.messageTransformer = new MessageTransformer(
159-
packetTransformer.outboundProtobufs$,
160-
packet => packetTransformer.writeInboundProtobuf(packet)
159+
packetTransformer.protobufs$,
160+
packet => packetTransformer.writeProtobuf(packet)
161161
);
162162
}
163163

lib/src/compiler/sync.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {activeDeprecationOptions} from '../deprecations';
1818
import {Dispatcher} from '../dispatcher';
1919
import {FunctionRegistry} from '../function-registry';
2020
import {ImporterRegistry} from '../importer-registry';
21-
import {MessageTransformer} from '../message-transformer';
21+
import {HostMessageTransformer as MessageTransformer} from '../message-transformer';
2222
import {PacketTransformer} from '../packet-transformer';
2323
import {SyncProcess} from '../sync-process';
2424
import * as utils from '../utils';
@@ -176,8 +176,8 @@ export class Compiler {
176176
this.writeStdin(buffer);
177177
});
178178
this.messageTransformer = new MessageTransformer(
179-
packetTransformer.outboundProtobufs$,
180-
packet => packetTransformer.writeInboundProtobuf(packet)
179+
packetTransformer.protobufs$,
180+
packet => packetTransformer.writeProtobuf(packet)
181181
);
182182
}
183183

lib/src/compiler/utils.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
removeLegacyImporterFromSpan,
1818
} from '../legacy/utils';
1919
import {Logger} from '../logger';
20-
import {MessageTransformer} from '../message-transformer';
20+
import {HostMessageTransformer as MessageTransformer} from '../message-transformer';
2121
import * as utils from '../utils';
2222
import * as proto from '../vendor/embedded_sass_pb';
2323
import {SourceSpan} from '../vendor/sass';
@@ -55,8 +55,8 @@ export function createDispatcher<sync extends 'sync' | 'async'>(
5555
): Dispatcher<sync> {
5656
return new Dispatcher<sync>(
5757
compilationId,
58-
messageTransformer.outboundMessages$,
59-
message => messageTransformer.writeInboundMessage(message),
58+
messageTransformer.messages$,
59+
message => messageTransformer.writeMessage(message),
6060
handlers
6161
);
6262
}

lib/src/message-transformer.test.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as varint from 'varint';
77
import {create, toBinary} from '@bufbuild/protobuf';
88

99
import {expectObservableToError} from '../../test/utils';
10-
import {MessageTransformer} from './message-transformer';
10+
import {HostMessageTransformer as MessageTransformer} from './message-transformer';
1111
import * as proto from './vendor/embedded_sass_pb';
1212

1313
describe('message transformer', () => {
@@ -41,7 +41,7 @@ describe('message transformer', () => {
4141

4242
it('encodes an InboundMessage to buffer', () => {
4343
const message = validInboundMessage('a {b: c}');
44-
messages.writeInboundMessage([1234, message]);
44+
messages.writeMessage([1234, message]);
4545
expect(encodedProtobufs).toEqual([
4646
Uint8Array.from([
4747
...varint.encode(1234),
@@ -62,7 +62,7 @@ describe('message transformer', () => {
6262
});
6363

6464
it('decodes buffer to OutboundMessage', done => {
65-
messages.outboundMessages$.subscribe({
65+
messages.messages$.subscribe({
6666
next: message => decodedMessages.push(message),
6767
complete: () => {
6868
expect(decodedMessages.length).toBe(1);
@@ -97,7 +97,7 @@ describe('message transformer', () => {
9797
describe('protocol error', () => {
9898
it('fails on invalid buffer', done => {
9999
expectObservableToError(
100-
messages.outboundMessages$,
100+
messages.messages$,
101101
'Compiler caused error: Invalid compilation ID varint: RangeError: ' +
102102
'Could not decode varint.',
103103
done

lib/src/message-transformer.ts

+83-47
Original file line numberDiff line numberDiff line change
@@ -5,84 +5,120 @@
55
import {Observable, Subject} from 'rxjs';
66
import {map} from 'rxjs/operators';
77
import {fromBinary, toBinary} from '@bufbuild/protobuf';
8+
import type {Message} from '@bufbuild/protobuf';
9+
import type {GenMessage} from '@bufbuild/protobuf/codegenv1';
810
import * as varint from 'varint';
911

1012
import {compilerError} from './utils';
11-
import {
12-
InboundMessage,
13-
InboundMessageSchema,
14-
OutboundMessage,
15-
OutboundMessageSchema,
16-
} from './vendor/embedded_sass_pb';
13+
import * as proto from './vendor/embedded_sass_pb';
1714

1815
/**
19-
* Encodes InboundMessages into protocol buffers and decodes protocol buffers
20-
* into OutboundMessages.
16+
* Encodes OutType into protocol buffers and decodes protocol buffers
17+
* into InType.
2118
*/
22-
export class MessageTransformer {
19+
class MessageTransformer<InType extends Message, OutType extends Message> {
2320
// The decoded messages are written to this Subject. It is publicly exposed
2421
// as a readonly Observable.
25-
private readonly outboundMessagesInternal$ = new Subject<
26-
[number, OutboundMessage]
27-
>();
22+
private readonly messagesInternal$ = new Subject<[number, InType]>();
2823

2924
/**
30-
* The OutboundMessages, decoded from protocol buffers. If this fails to
25+
* The InType messages, decoded from protocol buffers. If this fails to
3126
* decode a message, it will emit an error.
3227
*/
33-
readonly outboundMessages$ = this.outboundMessagesInternal$.pipe();
28+
readonly messages$ = this.messagesInternal$.pipe();
3429

3530
constructor(
36-
private readonly outboundProtobufs$: Observable<Uint8Array>,
37-
private readonly writeInboundProtobuf: (buffer: Uint8Array) => void
31+
private readonly InTypeSchema: GenMessage<InType>,
32+
private readonly OutTypeSchema: GenMessage<OutType>,
33+
private readonly protobufs$: Observable<Uint8Array>,
34+
private readonly writeProtobuf: (buffer: Uint8Array) => void
3835
) {
39-
this.outboundProtobufs$
40-
.pipe(map(decode))
41-
.subscribe(this.outboundMessagesInternal$);
36+
this.protobufs$
37+
.pipe(map(buffer => this.decode(buffer)))
38+
.subscribe(this.messagesInternal$);
4239
}
4340

4441
/**
45-
* Converts the inbound `compilationId` and `message` to a protocol buffer.
42+
* Converts the `compilationId` and OutType `message` to a protocol buffer.
4643
*/
47-
writeInboundMessage([compilationId, message]: [
48-
number,
49-
InboundMessage,
50-
]): void {
44+
writeMessage([compilationId, message]: [number, OutType]): void {
5145
const compilationIdLength = varint.encodingLength(compilationId);
52-
const encodedMessage = toBinary(InboundMessageSchema, message);
46+
const encodedMessage = toBinary(this.OutTypeSchema, message);
5347
const buffer = new Uint8Array(compilationIdLength + encodedMessage.length);
5448
varint.encode(compilationId, buffer);
5549
buffer.set(encodedMessage, compilationIdLength);
5650

5751
try {
58-
this.writeInboundProtobuf(buffer);
52+
this.writeProtobuf(buffer);
5953
} catch (error) {
60-
this.outboundMessagesInternal$.error(error);
54+
this.messagesInternal$.error(error);
55+
}
56+
}
57+
58+
// Decodes a protobuf `buffer` into a compilation ID and an InType message,
59+
// ensuring that all mandatory message fields are populated. Throws if `buffer`
60+
// cannot be decoded into a valid message, or if the message itself contains a
61+
// Protocol Error.
62+
private decode(buffer: Uint8Array): [number, InType] {
63+
let compilationId: number;
64+
try {
65+
compilationId = varint.decode(buffer);
66+
} catch (error) {
67+
throw compilerError(`Invalid compilation ID varint: ${error}`);
68+
}
69+
70+
try {
71+
return [
72+
compilationId,
73+
fromBinary(
74+
this.InTypeSchema,
75+
new Uint8Array(buffer.buffer, varint.decode.bytes)
76+
),
77+
];
78+
} catch (error) {
79+
throw compilerError(`Invalid protobuf: ${error}`);
6180
}
6281
}
6382
}
6483

65-
// Decodes a protobuf `buffer` into a compilation ID and an OutboundMessage,
66-
// ensuring that all mandatory message fields are populated. Throws if `buffer`
67-
// cannot be decoded into a valid message, or if the message itself contains a
68-
// Protocol Error.
69-
function decode(buffer: Uint8Array): [number, OutboundMessage] {
70-
let compilationId: number;
71-
try {
72-
compilationId = varint.decode(buffer);
73-
} catch (error) {
74-
throw compilerError(`Invalid compilation ID varint: ${error}`);
84+
/**
85+
* Encodes InboundMessage into protocol buffers and decodes protocol buffers
86+
* into OutboundMessage.
87+
*/
88+
export class HostMessageTransformer extends MessageTransformer<
89+
proto.OutboundMessage,
90+
proto.InboundMessage
91+
> {
92+
constructor(
93+
protobufs$: Observable<Uint8Array>,
94+
writeProtobuf: (buffer: Uint8Array) => void
95+
) {
96+
super(
97+
proto.OutboundMessageSchema,
98+
proto.InboundMessageSchema,
99+
protobufs$,
100+
writeProtobuf
101+
);
75102
}
103+
}
76104

77-
try {
78-
return [
79-
compilationId,
80-
fromBinary(
81-
OutboundMessageSchema,
82-
new Uint8Array(buffer.buffer, varint.decode.bytes)
83-
),
84-
];
85-
} catch (error) {
86-
throw compilerError(`Invalid protobuf: ${error}`);
105+
/**
106+
* Encodes OutboundMessage into protocol buffers and decodes protocol buffers
107+
* into InboundMessage.
108+
*/
109+
export class CompilerMessageTransformer extends MessageTransformer<
110+
proto.InboundMessage,
111+
proto.OutboundMessage
112+
> {
113+
constructor(
114+
protobufs$: Observable<Uint8Array>,
115+
writeProtobuf: (buffer: Uint8Array) => void
116+
) {
117+
super(
118+
proto.InboundMessageSchema,
119+
proto.OutboundMessageSchema,
120+
protobufs$,
121+
writeProtobuf
122+
);
87123
}
88124
}

lib/src/packet-transformer.test.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,29 @@ describe('packet transformer', () => {
2020
});
2121

2222
it('encodes an empty message', () => {
23-
packets.writeInboundProtobuf(Buffer.from([]));
23+
packets.writeProtobuf(Buffer.from([]));
2424

2525
expect(encodedBuffers).toEqual([Buffer.from([0])]);
2626
});
2727

2828
it('encodes a message of length 1', () => {
29-
packets.writeInboundProtobuf(Buffer.from([123]));
29+
packets.writeProtobuf(Buffer.from([123]));
3030

3131
expect(encodedBuffers).toEqual([Buffer.from([1, 123])]);
3232
});
3333

3434
it('encodes a message of length greater than 256', () => {
35-
packets.writeInboundProtobuf(Buffer.alloc(300, 1));
35+
packets.writeProtobuf(Buffer.alloc(300, 1));
3636

3737
expect(encodedBuffers).toEqual([
3838
Buffer.from([172, 2, ...new Array(300).fill(1)]),
3939
]);
4040
});
4141

4242
it('encodes multiple messages', () => {
43-
packets.writeInboundProtobuf(Buffer.from([10]));
44-
packets.writeInboundProtobuf(Buffer.from([20, 30]));
45-
packets.writeInboundProtobuf(Buffer.from([40, 50, 60]));
43+
packets.writeProtobuf(Buffer.from([10]));
44+
packets.writeProtobuf(Buffer.from([20, 30]));
45+
packets.writeProtobuf(Buffer.from([40, 50, 60]));
4646

4747
expect(encodedBuffers).toEqual([
4848
Buffer.from([1, 10]),
@@ -57,7 +57,7 @@ describe('packet transformer', () => {
5757

5858
function expectDecoding(expected: Buffer[], done: () => void): void {
5959
const actual: Buffer[] = [];
60-
packets.outboundProtobufs$.subscribe({
60+
packets.protobufs$.subscribe({
6161
next: protobuf => actual.push(protobuf),
6262
error: () => fail('expected correct decoding'),
6363
complete: () => {

lib/src/packet-transformer.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -26,32 +26,32 @@ export class PacketTransformer {
2626

2727
// The decoded protobufs are written to this Subject. It is publicly exposed
2828
// as a readonly Observable.
29-
private readonly outboundProtobufsInternal$ = new Subject<Buffer>();
29+
private readonly protobufsInternal$ = new Subject<Buffer>();
3030

3131
/**
3232
* The fully-decoded, outbound protobufs. If any errors are encountered
3333
* during encoding/decoding, this Observable will error out.
3434
*/
35-
readonly outboundProtobufs$ = this.outboundProtobufsInternal$.pipe();
35+
readonly protobufs$ = this.protobufsInternal$.pipe();
3636

3737
constructor(
38-
private readonly outboundBuffers$: Observable<Uint8Array>,
39-
private readonly writeInboundBuffer: (buffer: Buffer) => void
38+
private readonly buffers$: Observable<Uint8Array>,
39+
private readonly writeBuffer: (buffer: Buffer) => void
4040
) {
41-
this.outboundBuffers$
41+
this.buffers$
4242
.pipe(mergeMap(buffer => this.decode(buffer)))
43-
.subscribe(this.outboundProtobufsInternal$);
43+
.subscribe(this.protobufsInternal$);
4444
}
4545

4646
/**
4747
* Encodes a packet by pre-fixing `protobuf` with a header that describes its
4848
* length.
4949
*/
50-
writeInboundProtobuf(protobuf: Uint8Array): void {
50+
writeProtobuf(protobuf: Uint8Array): void {
5151
try {
5252
let length = protobuf.length;
5353
if (length === 0) {
54-
this.writeInboundBuffer(Buffer.alloc(1));
54+
this.writeBuffer(Buffer.alloc(1));
5555
return;
5656
}
5757

@@ -69,9 +69,9 @@ export class PacketTransformer {
6969
const packet = Buffer.alloc(header.length + protobuf.length);
7070
header.copy(packet);
7171
packet.set(protobuf, header.length);
72-
this.writeInboundBuffer(packet);
72+
this.writeBuffer(packet);
7373
} catch (error) {
74-
this.outboundProtobufsInternal$.error(error);
74+
this.protobufsInternal$.error(error);
7575
}
7676
}
7777

0 commit comments

Comments
 (0)