refactor: encoder functions deduplications

This commit is contained in:
Johann Schopplich
2025-10-29 10:23:31 +01:00
parent 8d238f8eeb
commit 78d2a67347
6 changed files with 43 additions and 53 deletions

View File

@@ -24,7 +24,7 @@ import {
parseKeyToken, parseKeyToken,
parsePrimitiveToken, parsePrimitiveToken,
} from './parser' } from './parser'
import { findClosingQuote } from './string-utils' import { findClosingQuote } from './utils'
import { import {
assertExpectedCount, assertExpectedCount,
validateNoExtraListItems, validateNoExtraListItems,

View File

@@ -21,7 +21,7 @@ import {
TAB, TAB,
TRUE_LITERAL, TRUE_LITERAL,
} from '../constants' } from '../constants'
import { findClosingQuote, hasUnquotedChar } from './string-utils' import { findClosingQuote, hasUnquotedChar } from './utils'
// #region Array header parsing // #region Array header parsing

View File

@@ -6,7 +6,7 @@ import type {
JsonValue, JsonValue,
ResolvedEncodeOptions, ResolvedEncodeOptions,
} from './types' } from './types'
import { LIST_ITEM_MARKER, LIST_ITEM_PREFIX } from './constants' import { LIST_ITEM_MARKER } from './constants'
import { import {
isArrayOfArrays, isArrayOfArrays,
isArrayOfObjects, isArrayOfObjects,
@@ -95,7 +95,8 @@ export function encodeArray(
// Primitive array // Primitive array
if (isArrayOfPrimitives(value)) { if (isArrayOfPrimitives(value)) {
encodeInlinePrimitiveArray(key, value, writer, depth, options) const formatted = encodeInlineArrayLine(value, options.delimiter, key, options.lengthMarker)
writer.push(depth, formatted)
return return
} }
@@ -126,21 +127,6 @@ export function encodeArray(
// #endregion // #endregion
// #region Primitive array encoding (inline)
export function encodeInlinePrimitiveArray(
prefix: string | undefined,
values: readonly JsonPrimitive[],
writer: LineWriter,
depth: Depth,
options: ResolvedEncodeOptions,
): void {
const formatted = encodeInlineArrayLine(values, options.delimiter, prefix, options.lengthMarker)
writer.push(depth, formatted)
}
// #endregion
// #region Array of arrays (expanded format) // #region Array of arrays (expanded format)
export function encodeArrayOfArraysAsListItems( export function encodeArrayOfArraysAsListItems(
@@ -156,7 +142,7 @@ export function encodeArrayOfArraysAsListItems(
for (const arr of values) { for (const arr of values) {
if (isArrayOfPrimitives(arr)) { if (isArrayOfPrimitives(arr)) {
const inline = encodeInlineArrayLine(arr, options.delimiter, undefined, options.lengthMarker) const inline = encodeInlineArrayLine(arr, options.delimiter, undefined, options.lengthMarker)
writer.push(depth + 1, `${LIST_ITEM_PREFIX}${inline}`) writer.pushListItem(depth + 1, inline)
} }
} }
} }
@@ -258,21 +244,7 @@ export function encodeMixedArrayAsListItems(
writer.push(depth, header) writer.push(depth, header)
for (const item of items) { for (const item of items) {
if (isJsonPrimitive(item)) { encodeListItemValue(item, writer, depth + 1, options)
// Direct primitive as list item
writer.push(depth + 1, `${LIST_ITEM_PREFIX}${encodePrimitive(item, options.delimiter)}`)
}
else if (isJsonArray(item)) {
// Direct array as list item
if (isArrayOfPrimitives(item)) {
const inline = encodeInlineArrayLine(item, options.delimiter, undefined, options.lengthMarker)
writer.push(depth + 1, `${LIST_ITEM_PREFIX}${inline}`)
}
}
else if (isJsonObject(item)) {
// Object as list item
encodeObjectAsListItem(item, writer, depth + 1, options)
}
} }
} }
@@ -289,13 +261,13 @@ export function encodeObjectAsListItem(obj: JsonObject, writer: LineWriter, dept
const firstValue = obj[firstKey]! const firstValue = obj[firstKey]!
if (isJsonPrimitive(firstValue)) { if (isJsonPrimitive(firstValue)) {
writer.push(depth, `${LIST_ITEM_PREFIX}${encodedKey}: ${encodePrimitive(firstValue, options.delimiter)}`) writer.pushListItem(depth, `${encodedKey}: ${encodePrimitive(firstValue, options.delimiter)}`)
} }
else if (isJsonArray(firstValue)) { else if (isJsonArray(firstValue)) {
if (isArrayOfPrimitives(firstValue)) { if (isArrayOfPrimitives(firstValue)) {
// Inline format for primitive arrays // Inline format for primitive arrays
const formatted = encodeInlineArrayLine(firstValue, options.delimiter, firstKey, options.lengthMarker) const formatted = encodeInlineArrayLine(firstValue, options.delimiter, firstKey, options.lengthMarker)
writer.push(depth, `${LIST_ITEM_PREFIX}${formatted}`) writer.pushListItem(depth, formatted)
} }
else if (isArrayOfObjects(firstValue)) { else if (isArrayOfObjects(firstValue)) {
// Check if array of objects can use tabular format // Check if array of objects can use tabular format
@@ -303,12 +275,12 @@ export function encodeObjectAsListItem(obj: JsonObject, writer: LineWriter, dept
if (header) { if (header) {
// Tabular format for uniform arrays of objects // Tabular format for uniform arrays of objects
const headerStr = formatHeader(firstValue.length, { key: firstKey, fields: header, delimiter: options.delimiter, lengthMarker: options.lengthMarker }) const headerStr = formatHeader(firstValue.length, { key: firstKey, fields: header, delimiter: options.delimiter, lengthMarker: options.lengthMarker })
writer.push(depth, `${LIST_ITEM_PREFIX}${headerStr}`) writer.pushListItem(depth, headerStr)
writeTabularRows(firstValue, header, writer, depth + 1, options) writeTabularRows(firstValue, header, writer, depth + 1, options)
} }
else { else {
// Fall back to list format for non-uniform arrays of objects // Fall back to list format for non-uniform arrays of objects
writer.push(depth, `${LIST_ITEM_PREFIX}${encodedKey}[${firstValue.length}]:`) writer.pushListItem(depth, `${encodedKey}[${firstValue.length}]:`)
for (const item of firstValue) { for (const item of firstValue) {
encodeObjectAsListItem(item, writer, depth + 1, options) encodeObjectAsListItem(item, writer, depth + 1, options)
} }
@@ -316,30 +288,21 @@ export function encodeObjectAsListItem(obj: JsonObject, writer: LineWriter, dept
} }
else { else {
// Complex arrays on separate lines (array of arrays, etc.) // Complex arrays on separate lines (array of arrays, etc.)
writer.push(depth, `${LIST_ITEM_PREFIX}${encodedKey}[${firstValue.length}]:`) writer.pushListItem(depth, `${encodedKey}[${firstValue.length}]:`)
// Encode array contents at depth + 1 // Encode array contents at depth + 1
for (const item of firstValue) { for (const item of firstValue) {
if (isJsonPrimitive(item)) { encodeListItemValue(item, writer, depth + 1, options)
writer.push(depth + 1, `${LIST_ITEM_PREFIX}${encodePrimitive(item, options.delimiter)}`)
}
else if (isJsonArray(item) && isArrayOfPrimitives(item)) {
const inline = encodeInlineArrayLine(item, options.delimiter, undefined, options.lengthMarker)
writer.push(depth + 1, `${LIST_ITEM_PREFIX}${inline}`)
}
else if (isJsonObject(item)) {
encodeObjectAsListItem(item, writer, depth + 1, options)
}
} }
} }
} }
else if (isJsonObject(firstValue)) { else if (isJsonObject(firstValue)) {
const nestedKeys = Object.keys(firstValue) const nestedKeys = Object.keys(firstValue)
if (nestedKeys.length === 0) { if (nestedKeys.length === 0) {
writer.push(depth, `${LIST_ITEM_PREFIX}${encodedKey}:`) writer.pushListItem(depth, `${encodedKey}:`)
} }
else { else {
writer.push(depth, `${LIST_ITEM_PREFIX}${encodedKey}:`) writer.pushListItem(depth, `${encodedKey}:`)
encodeObject(firstValue, writer, depth + 2, options) encodeObject(firstValue, writer, depth + 2, options)
} }
} }
@@ -352,3 +315,25 @@ export function encodeObjectAsListItem(obj: JsonObject, writer: LineWriter, dept
} }
// #endregion // #endregion
// #region List item encoding helpers
function encodeListItemValue(
value: JsonValue,
writer: LineWriter,
depth: Depth,
options: ResolvedEncodeOptions,
): void {
if (isJsonPrimitive(value)) {
writer.pushListItem(depth, encodePrimitive(value, options.delimiter))
}
else if (isJsonArray(value) && isArrayOfPrimitives(value)) {
const inline = encodeInlineArrayLine(value, options.delimiter, undefined, options.lengthMarker)
writer.pushListItem(depth, inline)
}
else if (isJsonObject(value)) {
encodeObjectAsListItem(value, writer, depth, options)
}
}
// #endregion

View File

@@ -7,9 +7,9 @@ import type {
} from './types' } from './types'
import { DEFAULT_DELIMITER } from './constants' import { DEFAULT_DELIMITER } from './constants'
import { decodeValueFromLines } from './decode/decoders' import { decodeValueFromLines } from './decode/decoders'
import { LineCursor, toParsedLines } from './decode/scanner'
import { encodeValue } from './encoders' import { encodeValue } from './encoders'
import { normalizeValue } from './normalize' import { normalizeValue } from './normalize'
import { LineCursor, toParsedLines } from './decode/scanner'
export { DEFAULT_DELIMITER, DELIMITERS } from './constants' export { DEFAULT_DELIMITER, DELIMITERS } from './constants'
export type { export type {

View File

@@ -1,4 +1,5 @@
import type { Depth } from './types' import type { Depth } from './types'
import { LIST_ITEM_PREFIX } from './constants'
export class LineWriter { export class LineWriter {
private readonly lines: string[] = [] private readonly lines: string[] = []
@@ -13,6 +14,10 @@ export class LineWriter {
this.lines.push(indent + content) this.lines.push(indent + content)
} }
pushListItem(depth: Depth, content: string): void {
this.push(depth, `${LIST_ITEM_PREFIX}${content}`)
}
toString(): string { toString(): string {
return this.lines.join('\n') return this.lines.join('\n')
} }