Skip to content

Commit

Permalink
Feat: Turnout list (#20)
Browse files Browse the repository at this point in the history
* Updated List Turnouts Command

Changed from `T` to `JT`

* Renamed `listTurnoutsCommand` to `turnoutsCommand`.

Added ability to request an individual Turnout

* Added Turnout List Parser

* Added Turnout Item Parser
  • Loading branch information
dcyoung-dev authored Jan 5, 2024
1 parent b4fc37c commit cdb7557
Show file tree
Hide file tree
Showing 13 changed files with 276 additions and 20 deletions.
2 changes: 1 addition & 1 deletion src/commands/turnouts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ export * from './defineDCCTurnoutCommand.js'
export * from './defineServoTurnoutCommand.js'
export * from './defineVPINTurnoutCommand.js'
export * from './deleteTurnoutCommand.js'
export * from './listTurnoutsCommand.js'
export * from './turnoutsCommand.js'
9 changes: 0 additions & 9 deletions src/commands/turnouts/listTurnoutsCommand.ts

This file was deleted.

22 changes: 22 additions & 0 deletions src/commands/turnouts/turnoutsCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { makeCommandFromAttributes } from '../../utils/index.js'

interface TurnoutsCommandParams {
turnoutId?: number
}

const turnoutsCommandKey = 'JT'

/**
*
* @returns {string|{readonly returnString: string, readonly sendString: string, returnsKey: string, key: string}}
*/
export const turnoutsCommand: (params?: TurnoutsCommandParams) => string = ({ turnoutId } = {}) => {
return makeCommandFromAttributes([turnoutsCommandKey, turnoutId])
}

/**
* @deprecated Use turnoutsCommand instead
*/
export const listTurnoutsCommand: () => string = () => {
return turnoutsCommand()
}
4 changes: 3 additions & 1 deletion src/parsers/genericParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { eraseParser, storeParser } from './eeproms/index.js'
import { powerParser } from './powers/index.js'
import { locoParser, throttleParser } from './throttles/index.js'
import { decoderAddressParser } from './decoders/index.js'
import { turnoutDCCParser, turnoutParser } from './turnouts/index.js'
import { turnoutDCCParser, turnoutItemParser, turnoutListParser, turnoutParser } from './turnouts/index.js'

type ParserFunction = (command: string) => ParserResult<any>
type ParseResult = (command: string) => Promise<ParserResult<any>>
Expand Down Expand Up @@ -44,6 +44,8 @@ export const genericParser: GenericParser = () => {
throttleParser,
decoderAddressParser,
turnoutParser,
turnoutListParser,
turnoutItemParser,
turnoutDCCParser
]

Expand Down
2 changes: 2 additions & 0 deletions src/parsers/turnouts/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from './turnoutParser.js'
export * from './turnoutDCCParser.js'
export * from './turnoutListParser.js'
export * from './turnoutItemParser.js'
47 changes: 47 additions & 0 deletions src/parsers/turnouts/turnoutItemParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Command, parseCommand, removeDoubleQuotes } from '../../utils/index.js'
import {
FunctionName,
ParserResult,
ParserStatus
} from '../../types/index.js'
import { ParserAttributeError, ParserKeyError } from '../errors/index.js'
import { TurnoutState } from '../../commands/index.js'

interface TurnoutItemParams {
turnoutId: number
thrown: TurnoutState
description: string
}

export type TurnoutItemResult = ParserResult<TurnoutItemParams>
const turnoutItemParserKey = 'jT'

const parseFromCommand: (params: Command) => TurnoutItemResult = ({ key, attributes }) => {
const [turnoutId, state, description] = attributes

if (key !== turnoutItemParserKey || attributes.length !== 3) {
throw new ParserKeyError('turnoutItemParser', key)
}

if (!description.includes('"')) {
throw new ParserAttributeError('description', description, 'description must be wrapped in double quotes')
}

const thrown = parseInt(state)

return {
key: turnoutItemParserKey,
parser: FunctionName.TURNOUT_ITEM,
status: ParserStatus.SUCCESS,
params: {
turnoutId: parseInt(turnoutId),
thrown: thrown === 0 ? TurnoutState.CLOSED : TurnoutState.THROWN,
description: removeDoubleQuotes(description)
}
}
}

export const turnoutItemParser: (command: string) => TurnoutItemResult = (command) => {
const commandParams = parseCommand(command)
return parseFromCommand(commandParams)
}
41 changes: 41 additions & 0 deletions src/parsers/turnouts/turnoutListParser.ts
Original file line number Diff line number Diff line change
@@ -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 TurnoutListParams {
turnoutIds: number[]
}

export type TurnoutListResult = ParserResult<TurnoutListParams>
const turnoutListParserKey = 'jT'

const parseFromCommand: (params: Command) => TurnoutListResult = ({ key, attributes }) => {
const possibleTurnoutIds = attributes

if (key !== turnoutListParserKey) {
throw new ParserKeyError('turnoutListParser', key)
}

const turnoutIds = possibleTurnoutIds.map(turnoutIdString => {
const turnoutId = parseInt(turnoutIdString)
if (!isNaN(turnoutId) && isFinite(turnoutId)) {
return turnoutId
}

throw new Error(`Cab ID ${turnoutIdString} is not a number`)
})

return {
key: turnoutListParserKey,
parser: FunctionName.TURNOUT_LIST,
status: ParserStatus.SUCCESS,
params: {
turnoutIds
}
}
}

export const turnoutListParser: (command: string) => TurnoutListResult = (command) => {
const commandParams = parseCommand(command)
return parseFromCommand(commandParams)
}
2 changes: 2 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export enum FunctionName {
THROTTLE = 'throttleParser',
DECODER_ADDRESS = 'decoderAddress',
TURNOUT = 'turnoutParser',
TURNOUT_LIST = 'turnoutListParser',
TURNOUT_ITEM = 'turnoutItemParser',
TURNOUT_DCC = 'turnoutDCCParser'
}

Expand Down
9 changes: 0 additions & 9 deletions tests/unit/commands/turnouts/listTurnoutsCommand.test.ts

This file was deleted.

17 changes: 17 additions & 0 deletions tests/unit/commands/turnouts/turnoutsCommand.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { turnoutsCommand } from '../../../../src'

describe('turnoutsCommand()', function () {
it('is valid', () => {
const sendString = '<JT>'
const command = turnoutsCommand()
expect(command).toBe(sendString)
})

describe('for a specific Turnout', () => {
it('is valid', function () {
const sendString = '<JT 70>'
const command = turnoutsCommand({ turnoutId: 70 })
expect(command).toEqual(sendString)
})
})
})
61 changes: 61 additions & 0 deletions tests/unit/parsers/genericParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,67 @@ describe('genericParser()', () => {
}
}
},
{
command: '<jT>',
expectation: {
key: 'jT',
parser: FunctionName.TURNOUT_LIST,
params: {
turnoutIds: []
},
status: ParserStatus.SUCCESS
}
},

{
command: '<jT 200 10>',
expectation: {
key: 'jT',
parser: FunctionName.TURNOUT_LIST,
params: {
turnoutIds: [200, 10]
},
status: ParserStatus.SUCCESS
}
},
{
command: '<jT 1 22 333 4444>',
expectation: {
key: 'jT',
parser: FunctionName.TURNOUT_LIST,
params: {
turnoutIds: [1, 22, 333, 4444]
},
status: ParserStatus.SUCCESS
}
},
{
command: '<jT 70 1 "Turnout 70 Thrown">',
expectation: {
key: 'jT',
parser: FunctionName.TURNOUT_ITEM,
status: ParserStatus.SUCCESS,
params: {
turnoutId: 70,
thrown: TurnoutState.THROWN,
description: 'Turnout 70 Thrown'
}
}
},

{
command: '<jT 70 1 "10">',
expectation: {
key: 'jT',
parser: FunctionName.TURNOUT_ITEM,
status: ParserStatus.SUCCESS,
params: {
turnoutId: 70,
thrown: TurnoutState.THROWN,
description: '10'
}
}
},
{
command: '<H 1 0>',
expectation: {
Expand Down
32 changes: 32 additions & 0 deletions tests/unit/parsers/turnouts/turnoutItemParser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
FunctionName, ParserKeyError,
ParserStatus,
turnoutItemParser, TurnoutItemResult,
TurnoutState
} from '../../../../src'

describe('turnoutItemParser()', function () {
it('parses `<jT 1 0 "Description">`', () => {
const result = turnoutItemParser('<jT 1 0 "Description">')

const expected: TurnoutItemResult = {
key: 'jT',
parser: FunctionName.TURNOUT_ITEM,
status: ParserStatus.SUCCESS,
params: {
turnoutId: 1,
thrown: TurnoutState.CLOSED,
description: 'Description'
}
}
expect(result).toEqual(expected)
})

describe('with incorrect key', function () {
it('throws a ParserKeyError', function () {
expect(() => {
turnoutItemParser('<incorrect-key>')
}).toThrowError(ParserKeyError)
})
})
})
48 changes: 48 additions & 0 deletions tests/unit/parsers/turnouts/turnoutListParser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { FunctionName, ParserKeyError, ParserStatus, turnoutListParser, TurnoutListResult } from '../../../../src'

describe('turnoutListParser()', function () {
describe('without any Turnout Items', function () {
it('parses `<jT>`', () => {
const result = turnoutListParser('<jT>')

const expected: TurnoutListResult = {
key: 'jT',
parser: FunctionName.TURNOUT_LIST,
params: {
turnoutIds: []
},
status: ParserStatus.SUCCESS
}
expect(result).toEqual(expected)
})
})

describe('with Turnout Items', function () {
it('parses `<jT 1 22 333 4444>`', () => {
const result = turnoutListParser('<jT 1 22 333 4444>')

const expected: TurnoutListResult = {
key: 'jT',
parser: FunctionName.TURNOUT_LIST,
params: {
turnoutIds: [
1,
22,
333,
4444
]
},
status: ParserStatus.SUCCESS
}
expect(result).toEqual(expected)
})
})

describe('with incorrect key', function () {
it('throws a ParserKeyError', function () {
expect(() => {
turnoutListParser('<incorrect-key>')
}).toThrowError(ParserKeyError)
})
})
})

0 comments on commit cdb7557

Please sign in to comment.