From 5bf7ce14c84c35255201f4c4a2756411cc17d67b Mon Sep 17 00:00:00 2001 From: David Young Date: Fri, 5 Jan 2024 15:18:29 +0000 Subject: [PATCH 1/3] Updated Roster List and Item commands Changed from `` to `` --- src/commands/rosters/rosterCommand.ts | 2 +- tests/unit/commands/rosters/rosterCommand.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/rosters/rosterCommand.ts b/src/commands/rosters/rosterCommand.ts index f7d06a6..3bacd44 100644 --- a/src/commands/rosters/rosterCommand.ts +++ b/src/commands/rosters/rosterCommand.ts @@ -4,7 +4,7 @@ interface RosterCommandParams { cabId?: number } -const rosterSendKey = 'J' +const rosterSendKey = 'JR' export const rosterCommand: (params?: RosterCommandParams) => string = ({ cabId } = {}) => { return makeCommandFromAttributes([rosterSendKey, cabId]) diff --git a/tests/unit/commands/rosters/rosterCommand.test.ts b/tests/unit/commands/rosters/rosterCommand.test.ts index 9f0dfe4..802cacc 100644 --- a/tests/unit/commands/rosters/rosterCommand.test.ts +++ b/tests/unit/commands/rosters/rosterCommand.test.ts @@ -2,14 +2,14 @@ import { rosterCommand } from '../../../../src' describe('rosterCommand()', function () { it('is valid', function () { - const sendString = '' + const sendString = '' const command = rosterCommand() expect(command).toEqual(sendString) }) describe('for a specific Cab', () => { it('is valid', function () { - const sendString = '' + const sendString = '' const command = rosterCommand({ cabId: 70 }) expect(command).toEqual(sendString) }) From ad100a0081935801ece54cbe9b06970dc5139237 Mon Sep 17 00:00:00 2001 From: David Young Date: Fri, 5 Jan 2024 15:24:26 +0000 Subject: [PATCH 2/3] Updated Roster Item parser Changed from `` to `` --- src/parsers/rosters/rosterItemParser.ts | 2 +- tests/unit/parsers/genericParser.test.ts | 10 +++++----- tests/unit/parsers/rosters/rosterItemParser.test.ts | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/parsers/rosters/rosterItemParser.ts b/src/parsers/rosters/rosterItemParser.ts index 077581d..c68859f 100644 --- a/src/parsers/rosters/rosterItemParser.ts +++ b/src/parsers/rosters/rosterItemParser.ts @@ -18,7 +18,7 @@ interface RosterItemParams { } export type RosterItemResult = ParserResult -const rosterItemParserKey = 'j' +const rosterItemParserKey = 'jR' const functionButtonsParser = (param: string | null = null): RosterFunctionButtons => { if (param === null) { diff --git a/tests/unit/parsers/genericParser.test.ts b/tests/unit/parsers/genericParser.test.ts index 800ee90..0c7660a 100644 --- a/tests/unit/parsers/genericParser.test.ts +++ b/tests/unit/parsers/genericParser.test.ts @@ -18,7 +18,7 @@ describe('createParser()', () => { describe('when no parsers can parse the command', () => { it('should raise AggregateError', async () => { const parser = createParser([]) - const result = parser.parse('') + const result = parser.parse('') return await result.catch((e: any) => expect(e).toBeInstanceOf(AggregateError) ) @@ -31,10 +31,10 @@ describe('createParser()', () => { rosterItemParser ] const parser = createParser(parsers) - const result = await parser.parse('') + const result = await parser.parse('') const expected: RosterItemResult = { - key: 'j', + key: 'jR', parser: FunctionName.ROSTER_ITEM, params: { cabId: 70, @@ -123,9 +123,9 @@ describe('genericParser()', () => { } }, { - command: '', + command: '', expectation: { - key: 'j', + key: 'jR', parser: FunctionName.ROSTER_ITEM, params: { cabId: 70, diff --git a/tests/unit/parsers/rosters/rosterItemParser.test.ts b/tests/unit/parsers/rosters/rosterItemParser.test.ts index 8b5a3f2..6ea7edb 100644 --- a/tests/unit/parsers/rosters/rosterItemParser.test.ts +++ b/tests/unit/parsers/rosters/rosterItemParser.test.ts @@ -9,11 +9,11 @@ import { describe('rosterItemParser()', function () { describe('without function descriptions', function () { - it('parses ``', () => { - const result = rosterItemParser('') + it('parses ``', () => { + const result = rosterItemParser('') const expected: RosterItemResult = { - key: 'j', + key: 'jR', parser: FunctionName.ROSTER_ITEM, params: { cabId: 70, @@ -27,11 +27,11 @@ describe('rosterItemParser()', function () { }) describe('with function descriptions', function () { - it('parses ``', () => { - const result = rosterItemParser('') + it('parses ``', () => { + const result = rosterItemParser('') const expected: RosterItemResult = { - key: 'j', + key: 'jR', parser: FunctionName.ROSTER_ITEM, params: { cabId: 70, From d5774f58ffd855d500ecdd0a30a0afbd3a43f407 Mon Sep 17 00:00:00 2001 From: David Young Date: Fri, 5 Jan 2024 16:19:56 +0000 Subject: [PATCH 3/3] Added Roster List Parser This includes reworking the Roster Item Parser to handle the commands correctly --- src/parsers/genericParser.ts | 3 +- src/parsers/rosters/index.ts | 1 + src/parsers/rosters/rosterItemParser.ts | 14 +++-- src/parsers/rosters/rosterListParser.ts | 41 ++++++++++++++ src/types/index.ts | 1 + src/utils/index.ts | 1 + src/utils/parseCommand.ts | 4 +- src/utils/removeQuotes.ts | 3 ++ tests/unit/parsers/genericParser.test.ts | 52 ++++++++++++++++++ .../parsers/rosters/rosterListParser.test.ts | 54 +++++++++++++++++++ tests/unit/utils/parseCommand.test.ts | 6 +-- 11 files changed, 168 insertions(+), 12 deletions(-) create mode 100644 src/parsers/rosters/rosterListParser.ts create mode 100644 src/utils/removeQuotes.ts create mode 100644 tests/unit/parsers/rosters/rosterListParser.test.ts diff --git a/src/parsers/genericParser.ts b/src/parsers/genericParser.ts index 5f5dd28..1e9b7a1 100644 --- a/src/parsers/genericParser.ts +++ b/src/parsers/genericParser.ts @@ -1,5 +1,5 @@ import { ParserResult } from '../types/index.js' -import { rosterItemParser } from './rosters/index.js' +import { rosterItemParser, rosterListParser } from './rosters/index.js' import { eraseParser, storeParser } from './eeproms/index.js' import { powerParser } from './powers/index.js' import { locoParser, throttleParser } from './throttles/index.js' @@ -39,6 +39,7 @@ export const genericParser: GenericParser = () => { locoParser, powerParser, rosterItemParser, + rosterListParser, storeParser, throttleParser, decoderAddressParser, diff --git a/src/parsers/rosters/index.ts b/src/parsers/rosters/index.ts index 1848d10..fa9c14c 100644 --- a/src/parsers/rosters/index.ts +++ b/src/parsers/rosters/index.ts @@ -1 +1,2 @@ export * from './rosterItemParser.js' +export * from './rosterListParser.js' diff --git a/src/parsers/rosters/rosterItemParser.ts b/src/parsers/rosters/rosterItemParser.ts index c68859f..8bd7ad8 100644 --- a/src/parsers/rosters/rosterItemParser.ts +++ b/src/parsers/rosters/rosterItemParser.ts @@ -1,4 +1,4 @@ -import { Command, parseCommand } from '../../utils/index.js' +import { Command, parseCommand, removeDoubleQuotes } from '../../utils/index.js' import { FunctionButton, FunctionButtonKind, @@ -7,7 +7,7 @@ import { ParserResult, ParserStatus } from '../../types/index.js' -import { ParserKeyError } from '../errors/index.js' +import { ParserAttributeError, ParserKeyError } from '../errors/index.js' type RosterFunctionButton = Pick type RosterFunctionButtons = FunctionButtons @@ -31,7 +31,7 @@ const functionButtonsParser = (param: string | null = null): RosterFunctionButto const [display, isPress] = button.split(/(\*)/).reverse() const kind = (isPress == null && !isPress) ? FunctionButtonKind.TOGGLE : FunctionButtonKind.PRESS accum[index] = { - display, + display: removeDoubleQuotes(display), kind } return accum @@ -41,10 +41,14 @@ const functionButtonsParser = (param: string | null = null): RosterFunctionButto const parseFromCommand: (params: Command) => RosterItemResult = ({ key, attributes }) => { const [cabId, display, rawFunctionButtons] = attributes - if (key !== rosterItemParserKey) { + if (key !== rosterItemParserKey || attributes.length > 3) { throw new ParserKeyError('rosterItemParser', key) } + if (!display.includes('"')) { + throw new ParserAttributeError('display', display, 'display must be wrapped in double quotes') + } + const functionButtons = functionButtonsParser(rawFunctionButtons) return { @@ -53,7 +57,7 @@ const parseFromCommand: (params: Command) => RosterItemResult = ({ key, attribut status: ParserStatus.SUCCESS, params: { cabId: parseInt(cabId), - display, + display: removeDoubleQuotes(display), functionButtons } } diff --git a/src/parsers/rosters/rosterListParser.ts b/src/parsers/rosters/rosterListParser.ts new file mode 100644 index 0000000..1ff25e1 --- /dev/null +++ b/src/parsers/rosters/rosterListParser.ts @@ -0,0 +1,41 @@ +import { Command, parseCommand } from '../../utils/index.js' +import { FunctionName, ParserResult, ParserStatus } from '../../types/index.js' +import { ParserKeyError } from '../errors/index.js' + +interface RosterListParams { + cabIds: number[] +} + +export type RosterListResult = ParserResult +const rosterListParserKey = 'jR' + +const parseFromCommand: (params: Command) => RosterListResult = ({ key, attributes }) => { + const possibleCabIds = attributes + + if (key !== rosterListParserKey) { + throw new ParserKeyError('rosterListParser', key) + } + + const cabIds = possibleCabIds.map(cabIdString => { + const cabId = parseInt(cabIdString) + if (!isNaN(cabId) && isFinite(cabId)) { + return cabId + } + + throw new Error(`Cab ID ${cabIdString} is not a number`) + }) + + return { + key: rosterListParserKey, + parser: FunctionName.ROSTER_LIST, + status: ParserStatus.SUCCESS, + params: { + cabIds + } + } +} + +export const rosterListParser: (command: string) => RosterListResult = (command) => { + const commandParams = parseCommand(command) + return parseFromCommand(commandParams) +} diff --git a/src/types/index.ts b/src/types/index.ts index 1332cb6..62508c2 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -19,6 +19,7 @@ export enum FunctionName { LOCO = 'locoParser', POWER = 'powerParser', ROSTER_ITEM = 'rosterItemParser', + ROSTER_LIST = 'rosterListParser', THROTTLE = 'throttleParser', DECODER_ADDRESS = 'decoderAddress', TURNOUT = 'turnoutParser', diff --git a/src/utils/index.ts b/src/utils/index.ts index 4013335..b0b4091 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,4 @@ export * from './makeCommand.js' export * from './parseAddress.js' export * from './parseCommand.js' +export * from './removeQuotes.js' diff --git a/src/utils/parseCommand.ts b/src/utils/parseCommand.ts index ca832fc..5c700d0 100644 --- a/src/utils/parseCommand.ts +++ b/src/utils/parseCommand.ts @@ -23,9 +23,7 @@ function splitBySpaceOrQuote (cleanedParams: string): string[] { } function getStrings (cleanedParams: string): string[] { - return splitBySpaceOrQuote(cleanedParams).map((part) => { - return part.replaceAll('"', '') - }) + return splitBySpaceOrQuote(cleanedParams) } export function parseCommand (command: string): Command { diff --git a/src/utils/removeQuotes.ts b/src/utils/removeQuotes.ts new file mode 100644 index 0000000..a9accb5 --- /dev/null +++ b/src/utils/removeQuotes.ts @@ -0,0 +1,3 @@ +export function removeDoubleQuotes (str: string): string { + return str.replaceAll('"', '') +} diff --git a/tests/unit/parsers/genericParser.test.ts b/tests/unit/parsers/genericParser.test.ts index 0c7660a..3be7d3c 100644 --- a/tests/unit/parsers/genericParser.test.ts +++ b/tests/unit/parsers/genericParser.test.ts @@ -148,6 +148,58 @@ describe('genericParser()', () => { status: ParserStatus.SUCCESS } }, + { + command: '', + expectation: { + key: 'jR', + parser: FunctionName.ROSTER_LIST, + params: { + cabIds: [ + 1, + 22, + 333, + 4444 + ] + }, + status: ParserStatus.SUCCESS + } + }, + { + command: '', + expectation: { + key: 'jR', + parser: FunctionName.ROSTER_LIST, + params: { + cabIds: [] + }, + status: ParserStatus.SUCCESS + } + }, + + { + command: '', + expectation: { + key: 'jR', + parser: FunctionName.ROSTER_LIST, + params: { + cabIds: [200, 10] + }, + status: ParserStatus.SUCCESS + } + }, + { + command: '', + expectation: { + key: 'jR', + parser: FunctionName.ROSTER_ITEM, + params: { + cabId: 200, + display: '10', + functionButtons: {} + }, + status: ParserStatus.SUCCESS + } + }, { command: '', expectation: { diff --git a/tests/unit/parsers/rosters/rosterListParser.test.ts b/tests/unit/parsers/rosters/rosterListParser.test.ts new file mode 100644 index 0000000..4ba112f --- /dev/null +++ b/tests/unit/parsers/rosters/rosterListParser.test.ts @@ -0,0 +1,54 @@ +import { + FunctionName, + ParserKeyError, + ParserStatus, + rosterListParser, + RosterListResult +} from '../../../../src' + +describe('rosterListParser()', function () { + describe('without any Roster Items', function () { + it('parses ``', () => { + const result = rosterListParser('') + + const expected: RosterListResult = { + key: 'jR', + parser: FunctionName.ROSTER_LIST, + params: { + cabIds: [] + }, + status: ParserStatus.SUCCESS + } + expect(result).toEqual(expected) + }) + }) + + describe('with Roster Items', function () { + it('parses ``', () => { + const result = rosterListParser('') + + const expected: RosterListResult = { + key: 'jR', + parser: FunctionName.ROSTER_LIST, + params: { + cabIds: [ + 1, + 22, + 333, + 4444 + ] + }, + status: ParserStatus.SUCCESS + } + expect(result).toEqual(expected) + }) + }) + + describe('with incorrect key', function () { + it('throws a ParserKeyError', function () { + expect(() => { + rosterListParser('') + }).toThrowError(ParserKeyError) + }) + }) +}) diff --git a/tests/unit/utils/parseCommand.test.ts b/tests/unit/utils/parseCommand.test.ts index 8c7608b..a273fc0 100644 --- a/tests/unit/utils/parseCommand.test.ts +++ b/tests/unit/utils/parseCommand.test.ts @@ -51,10 +51,10 @@ describe('parseCommand()', () => { describe('with string values', () => { it('returns a key and string attributes', () => { - expect(parseCommand('')) + expect(parseCommand('')) .toEqual({ - key: 'j', - attributes: ['70', 'My Loco', 'Other Parts'] + key: 'jR', + attributes: ['70', '"My Loco"', '"Other Parts"'] }) }) })