Skip to content

Commit 60381a9

Browse files
authored
Improve type guards for juggling command formats (#71)
Apply better pattern for typescript stuff!
1 parent f471b1f commit 60381a9

7 files changed

Lines changed: 109 additions & 46 deletions

File tree

src/Commands/Messages.ts

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,15 @@ export interface MessageTransformer<TMessage extends Conf.MessageArrayLike> {
1616
transformerType: Conf.MessageArrayLikeType;
1717
combineMessages(...messages: TMessage[]): TMessage;
1818

19-
messageToString(message: TMessage): string;
20-
messageToUint8Array(message: TMessage): Uint8Array;
19+
as<TTarget extends Conf.MessageArrayLike>(
20+
msg: Conf.MessageArrayLike,
21+
targetType: TTarget,
22+
): TTarget;
23+
24+
asType<TTarget extends Conf.MessageArrayLike>(
25+
msg: Conf.MessageArrayLike,
26+
targetType: Conf.MessageArrayLikeType,
27+
): TTarget;
2128
}
2229

2330
export class RawMessageTransformer implements MessageTransformer<Uint8Array> {
@@ -34,12 +41,32 @@ export class RawMessageTransformer implements MessageTransformer<Uint8Array> {
3441
).buffer;
3542
}
3643

37-
messageToString(message: Uint8Array): string {
38-
return asString(message);
44+
as<TTarget extends Conf.MessageArrayLike>(
45+
msg: Conf.MessageArrayLike,
46+
targetType: TTarget,
47+
): TTarget {
48+
if (typeof targetType === "string") {
49+
return asString(msg) as TTarget;
50+
} else if (targetType instanceof Uint8Array) {
51+
return msg as TTarget;
52+
} else {
53+
throw new Error("Unknown message type not implemented!");
54+
}
3955
}
4056

41-
messageToUint8Array(message: Uint8Array): Uint8Array {
42-
return message;
57+
asType<TTarget extends Conf.MessageArrayLike>(
58+
msg: Conf.MessageArrayLike,
59+
targetType: Conf.MessageArrayLikeType,
60+
): TTarget {
61+
switch(targetType) {
62+
default:
63+
Util.exhaustiveMatchGuard(targetType);
64+
break;
65+
case 'string':
66+
return asString(msg) as TTarget;
67+
case 'Uint8Array':
68+
return msg as TTarget;
69+
}
4370
}
4471
}
4572

@@ -49,11 +76,33 @@ export class StringMessageTransformer implements MessageTransformer<string> {
4976
combineMessages(...messages: string[]): string {
5077
return messages.join('');
5178
}
52-
messageToString(message: string): string {
53-
return message;
79+
80+
as<TTarget extends Conf.MessageArrayLike>(
81+
msg: Conf.MessageArrayLike,
82+
targetType: TTarget,
83+
): TTarget {
84+
if (typeof targetType === "string") {
85+
return msg as TTarget;
86+
} else if (targetType instanceof Uint8Array) {
87+
return asUint8Array(msg) as TTarget;
88+
} else {
89+
throw new Error("Unknown message type not implemented!");
90+
}
5491
}
55-
messageToUint8Array(message: string): Uint8Array {
56-
return asUint8Array(message);
92+
93+
asType<TTarget extends Conf.MessageArrayLike>(
94+
msg: Conf.MessageArrayLike,
95+
targetType: Conf.MessageArrayLikeType,
96+
): TTarget {
97+
switch(targetType) {
98+
default:
99+
Util.exhaustiveMatchGuard(targetType);
100+
break;
101+
case 'string':
102+
return msg as TTarget;
103+
case 'Uint8Array':
104+
return asUint8Array(msg) as TTarget;
105+
}
57106
}
58107
}
59108

@@ -90,6 +139,21 @@ export function asTargetMessageType<TMessage extends Conf.MessageArrayLike>(
90139
}
91140
}
92141

142+
export function asTargetMessageLikeType<TMessage extends Conf.MessageArrayLike>(
143+
msg: Conf.MessageArrayLike,
144+
targetType: Conf.MessageArrayLikeType,
145+
): TMessage {
146+
switch(targetType) {
147+
default:
148+
Util.exhaustiveMatchGuard(targetType);
149+
break;
150+
case 'Uint8Array':
151+
return asUint8Array(msg) as TMessage;
152+
case 'string':
153+
return asString(msg) as TMessage;
154+
}
155+
}
156+
93157
export type AwaitedCommand = {
94158
cmd: IPrinterCommand,
95159
promise: Promise<boolean>,

src/Languages/Epl/Messages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export function handleMessage<TReceived extends Conf.MessageArrayLike>(
1515
messages: [],
1616
remainder: message,
1717
}
18-
const msg = Cmds.asString(message);
18+
const msg = Cmds.asTargetMessageLikeType<string>(message, 'string');
1919
let remainder = msg;
2020
if (msg === undefined || msg.length === 0) { return result; }
2121

src/Languages/Zpl/Messages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function handleMessage<TReceived extends Conf.MessageArrayLike>(
1313
messages: [],
1414
remainder: message,
1515
}
16-
const msg = Cmds.asString(message).trimStart();
16+
const msg = Cmds.asTargetMessageLikeType<string>(message, 'string').trimStart();
1717
let remainder = msg;
1818
if (msg === undefined || msg.length === 0) { return result; }
1919

src/Printer.ts

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,6 @@ export interface LabelPrinterEventMap {
1212
reportedError: CustomEvent<Cmds.IErrorMessage>;
1313
}
1414

15-
function promiseWithTimeout<T>(
16-
promise: Promise<T>,
17-
ms: number,
18-
timeoutError = new Error('Promise timed out')
19-
): Promise<T> {
20-
// create a promise that rejects in milliseconds
21-
const timeout = new Promise<never>((_, reject) => {
22-
setTimeout(() => {
23-
reject(timeoutError);
24-
}, ms);
25-
});
26-
27-
// returns a race between timeout and the passed promise
28-
return Promise.race<T>([promise, timeout]);
29-
}
30-
3115
/** Type alias for a Label Printer that communicates over USB. */
3216
export type LabelPrinterUsb = LabelPrinter<Uint8Array>;
3317

@@ -339,34 +323,22 @@ export class LabelPrinter<TChannelType extends Conf.MessageArrayLike> extends Ev
339323
});
340324

341325
this.logResultIfDebug(() => {
342-
const debugMsg = Cmds.asString(transaction.commands);
326+
const debugMsg = this._channelMessageTransformer.asType(transaction.commands, 'string');
343327
return `Transaction being sent to printer:\n${debugMsg}\n--end of transaction--`;
344328
});
345329

346-
// TODO: Better type guards??
347-
let sendCmds: TChannelType;
348-
switch(this._channelType) {
349-
default:
350-
Util.exhaustiveMatchGuard(this._channelType);
351-
break;
352-
case 'Uint8Array':
353-
sendCmds = Cmds.asUint8Array(transaction.commands) as TChannelType;
354-
break;
355-
case 'string':
356-
sendCmds = Cmds.asString(transaction.commands) as TChannelType;
357-
break;
358-
}
359-
360-
await promiseWithTimeout(
361-
this._channel.send(sendCmds),
330+
await Util.promiseWithTimeout(
331+
this._channel.send(
332+
Cmds.asTargetMessageLikeType(transaction.commands, this._channelType)
333+
),
362334
5000,
363335
new Mux.DeviceCommunicationError(`Timed out sending commands to printer, is there a problem with the printer?`)
364336
);
365337

366338
try {
367339
if (this._awaitedCommands.length > 0) {
368340
this.logIfDebug(`Awaiting response to ${this._awaitedCommands.length} commands for up to ${this._awaitedCommandTimeoutMS}ms...`);
369-
await promiseWithTimeout(
341+
await Util.promiseWithTimeout(
370342
Promise.all(this._awaitedCommands.map(c => c.promise)),
371343
this._awaitedCommandTimeoutMS,
372344
new Mux.DeviceCommunicationError(`Timed out waiting for sent command response, expected ${this._awaitedCommands.length} responses.`)

src/Util/PromiseUtils.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { expect, describe, it } from 'vitest';
2+
import { promiseWithTimeout } from './PromiseUtils.js';
3+
4+
5+
describe('promiseWithtimeout', () => {
6+
it('Completes a promise before a timeout', async () => {
7+
const complete = Promise.resolve(true);
8+
const out = await promiseWithTimeout(complete, 100);
9+
expect(out).toBe(await complete);
10+
})
11+
});

src/Util/PromiseUtils.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export function promiseWithTimeout<T>(
2+
promise: Promise<T>,
3+
ms: number,
4+
timeoutError = new Error('Promise timed out')
5+
): Promise<T> {
6+
// create a promise that rejects in milliseconds
7+
const timeout = new Promise<never>((_, reject) => {
8+
setTimeout(() => {
9+
reject(timeoutError);
10+
}, ms);
11+
});
12+
13+
// returns a race between timeout and the passed promise
14+
return Promise.race<T>([promise, timeout]);
15+
}

src/Util/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from './EnumUtils.js';
44
export * from './NumericRange.js';
55
export * from './StringUtils.js';
66
export * from './WebZlpError.js';
7+
export * from './PromiseUtils.js';

0 commit comments

Comments
 (0)