refactor: decoding source files

This commit is contained in:
Johann Schopplich
2025-10-29 10:12:13 +01:00
parent c4f00bd69f
commit 8d238f8eeb
6 changed files with 268 additions and 124 deletions

97
src/decode/validation.ts Normal file
View File

@@ -0,0 +1,97 @@
import type { ArrayHeaderInfo, Delimiter, Depth, ResolvedDecodeOptions } from '../types'
import type { LineCursor } from './scanner'
import { COLON, LIST_ITEM_PREFIX } from '../constants'
/**
* Asserts that the actual count matches the expected count in strict mode.
*
* @param actual The actual count
* @param expected The expected count
* @param itemType The type of items being counted (e.g., 'list array items', 'tabular rows')
* @param options Decode options
* @throws RangeError if counts don't match in strict mode
*/
export function assertExpectedCount(
actual: number,
expected: number,
itemType: string,
options: ResolvedDecodeOptions,
): void {
if (options.strict && actual !== expected) {
throw new RangeError(`Expected ${expected} ${itemType}, but got ${actual}`)
}
}
/**
* Validates that there are no extra list items beyond the expected count.
*
* @param cursor The line cursor
* @param itemDepth The expected depth of items
* @param expectedCount The expected number of items
* @throws RangeError if extra items are found
*/
export function validateNoExtraListItems(
cursor: LineCursor,
itemDepth: Depth,
expectedCount: number,
): void {
if (cursor.atEnd())
return
const nextLine = cursor.peek()
if (nextLine && nextLine.depth === itemDepth && nextLine.content.startsWith(LIST_ITEM_PREFIX)) {
throw new RangeError(`Expected ${expectedCount} list array items, but found more`)
}
}
/**
* Checks if a line represents a data row (as opposed to a key-value pair) in a tabular array.
*
* @param content The line content
* @param delimiter The delimiter used in the table
* @returns true if the line is a data row, false if it's a key-value pair
*/
export function isDataRow(content: string, delimiter: Delimiter): boolean {
const colonPos = content.indexOf(COLON)
const delimiterPos = content.indexOf(delimiter)
// No colon = definitely a data row
if (colonPos === -1) {
return true
}
// Has delimiter and it comes before colon = data row
if (delimiterPos !== -1 && delimiterPos < colonPos) {
return true
}
// Colon before delimiter or no delimiter = key-value pair
return false
}
/**
* Validates that there are no extra tabular rows beyond the expected count.
*
* @param cursor The line cursor
* @param rowDepth The expected depth of rows
* @param header The array header info containing length and delimiter
* @throws RangeError if extra rows are found
*/
export function validateNoExtraTabularRows(
cursor: LineCursor,
rowDepth: Depth,
header: ArrayHeaderInfo,
): void {
if (cursor.atEnd())
return
const nextLine = cursor.peek()
if (
nextLine
&& nextLine.depth === rowDepth
&& !nextLine.content.startsWith(LIST_ITEM_PREFIX)
&& isDataRow(nextLine.content, header.delimiter)
) {
throw new RangeError(`Expected ${header.length} tabular rows, but found more`)
}
}