- parseInteger() now rejects malformed input like '120oops' using /^\d+$/ regex - New parseActiveValue() validates active flag is exactly '0' or '1' - Added regression tests for malformed integers, negative values, empty fields, non-binary active flags
136 lines
3.3 KiB
TypeScript
136 lines
3.3 KiB
TypeScript
import type { TmuxPaneInfo } from "./types"
|
|
|
|
const MANDATORY_PANE_FIELD_COUNT = 8
|
|
|
|
type ParsedPaneState = {
|
|
windowWidth: number
|
|
windowHeight: number
|
|
panes: TmuxPaneInfo[]
|
|
}
|
|
|
|
type ParsedPaneLine = {
|
|
pane: TmuxPaneInfo
|
|
windowWidth: number
|
|
windowHeight: number
|
|
}
|
|
|
|
type MandatoryPaneFields = [
|
|
paneId: string,
|
|
widthString: string,
|
|
heightString: string,
|
|
leftString: string,
|
|
topString: string,
|
|
activeString: string,
|
|
windowWidthString: string,
|
|
windowHeightString: string,
|
|
]
|
|
|
|
export function parsePaneStateOutput(stdout: string): ParsedPaneState | null {
|
|
const lines = stdout
|
|
.split("\n")
|
|
.map((line) => line.replace(/\r$/, ""))
|
|
.filter((line) => line.length > 0)
|
|
|
|
if (lines.length === 0) return null
|
|
|
|
const parsedPaneLines = lines
|
|
.map(parsePaneLine)
|
|
.filter((parsedPaneLine): parsedPaneLine is ParsedPaneLine => parsedPaneLine !== null)
|
|
|
|
if (parsedPaneLines.length === 0) return null
|
|
|
|
const latestPaneLine = parsedPaneLines[parsedPaneLines.length - 1]
|
|
if (!latestPaneLine) return null
|
|
|
|
return {
|
|
windowWidth: latestPaneLine.windowWidth,
|
|
windowHeight: latestPaneLine.windowHeight,
|
|
panes: parsedPaneLines.map(({ pane }) => pane),
|
|
}
|
|
}
|
|
|
|
function parsePaneLine(line: string): ParsedPaneLine | null {
|
|
const fields = line.split("\t")
|
|
const mandatoryFields = getMandatoryPaneFields(fields)
|
|
if (!mandatoryFields) return null
|
|
|
|
const [paneId, widthString, heightString, leftString, topString, activeString, windowWidthString, windowHeightString] = mandatoryFields
|
|
|
|
const width = parseInteger(widthString)
|
|
const height = parseInteger(heightString)
|
|
const left = parseInteger(leftString)
|
|
const top = parseInteger(topString)
|
|
const isActive = parseActiveValue(activeString)
|
|
const windowWidth = parseInteger(windowWidthString)
|
|
const windowHeight = parseInteger(windowHeightString)
|
|
|
|
if (
|
|
width === null ||
|
|
height === null ||
|
|
left === null ||
|
|
top === null ||
|
|
isActive === null ||
|
|
windowWidth === null ||
|
|
windowHeight === null
|
|
) {
|
|
return null
|
|
}
|
|
|
|
return {
|
|
pane: {
|
|
paneId,
|
|
width,
|
|
height,
|
|
left,
|
|
top,
|
|
title: fields.slice(MANDATORY_PANE_FIELD_COUNT).join("\t"),
|
|
isActive,
|
|
},
|
|
windowWidth,
|
|
windowHeight,
|
|
}
|
|
}
|
|
|
|
function getMandatoryPaneFields(fields: string[]): MandatoryPaneFields | null {
|
|
if (fields.length < MANDATORY_PANE_FIELD_COUNT) return null
|
|
|
|
const [paneId, widthString, heightString, leftString, topString, activeString, windowWidthString, windowHeightString] = fields
|
|
|
|
if (
|
|
paneId === undefined ||
|
|
widthString === undefined ||
|
|
heightString === undefined ||
|
|
leftString === undefined ||
|
|
topString === undefined ||
|
|
activeString === undefined ||
|
|
windowWidthString === undefined ||
|
|
windowHeightString === undefined
|
|
) {
|
|
return null
|
|
}
|
|
|
|
return [
|
|
paneId,
|
|
widthString,
|
|
heightString,
|
|
leftString,
|
|
topString,
|
|
activeString,
|
|
windowWidthString,
|
|
windowHeightString,
|
|
]
|
|
}
|
|
|
|
function parseInteger(value: string): number | null {
|
|
if (!/^\d+$/.test(value)) return null
|
|
|
|
const parsedValue = Number.parseInt(value, 10)
|
|
return Number.isNaN(parsedValue) ? null : parsedValue
|
|
}
|
|
|
|
function parseActiveValue(value: string): boolean | null {
|
|
if (value === "1") return true
|
|
if (value === "0") return false
|
|
return null
|
|
}
|