UNPKG

baldrick-broth

Version:
219 lines (196 loc) 5.53 kB
/** * Responsibilities: Implements built-in, non-shell command actions and helpers. * - Reads values from context, transforms them, and writes back via setDataValue * - Supports operations like get-property, boolean checks, split/range/concat * - Provides json-mask and Handlebars template expansions for data shaping */ import json_mask from 'json-mask'; import type { AnyCommand, AnyDataValue, Ctx } from './build-model.js'; import { coloration } from './coloration.js'; import { getSupportedProperty, isFalsy, isTruthy, setDataValue, } from './data-value-utils.js'; import type { LogMessage } from './log-model.js'; import { type Result, succeed, willFail } from './railway.js'; import { dasherizeTitle } from './string-utils.js'; import { getStringFromTemplate, mergeTemplateContext } from './templating.js'; type BasicExecution = Result<Ctx, LogMessage>; const getPropertyList = ( memoryId: string, ctx: Ctx, valuePaths: string[], ): Array<AnyDataValue | undefined> => valuePaths.map((path) => getSupportedProperty(memoryId, ctx, path)); const asStringOrBlank = (value: unknown): string => typeof value === 'string' ? value : ''; const asAnyArray = (value: unknown): AnyDataValue[] => Array.isArray(value) ? value : []; /** * Create a range starting with stop as other parameters are optional */ const range = (stop: number, start = 1, step = 1) => { const ranged = []; for (let index = start; index <= stop; index += step) { ranged.push(index); } return ranged; }; export const basicCommandExecution = ( memoryId: string, ctx: Ctx, anyCommand: AnyCommand, extra: Record<string, unknown>, ): BasicExecution => { const { a } = anyCommand; const success: BasicExecution = succeed(ctx); const name = anyCommand.name === undefined ? dasherizeTitle(anyCommand.title) : anyCommand.name; switch (a) { case 'get-property': { const value = getSupportedProperty(memoryId, ctx, anyCommand.value); setDataValue(memoryId, ctx, name, value); return success; } case 'some-truthy': { setDataValue( memoryId, ctx, name, getPropertyList(memoryId, ctx, anyCommand.values).some(isTruthy), ); return success; } case 'some-falsy': { setDataValue( memoryId, ctx, name, getPropertyList(memoryId, ctx, anyCommand.values).some(isFalsy), ); return success; } case 'every-truthy': { setDataValue( memoryId, ctx, name, getPropertyList(memoryId, ctx, anyCommand.values).every(isTruthy), ); return success; } case 'every-falsy': { setDataValue( memoryId, ctx, name, getPropertyList(memoryId, ctx, anyCommand.values).every(isFalsy), ); return success; } case 'not': { setDataValue( memoryId, ctx, name, isFalsy(getSupportedProperty(memoryId, ctx, anyCommand.value)), ); return success; } case 'split-string': { setDataValue( memoryId, ctx, name, asStringOrBlank( getSupportedProperty(memoryId, ctx, anyCommand.value), ).split(anyCommand.separator), ); return success; } case 'split-lines': { setDataValue( memoryId, ctx, name, asStringOrBlank(getSupportedProperty(memoryId, ctx, anyCommand.value)) .split('\n') .map((line) => line.trim()) .filter((line) => line.length > 0), ); return success; } case 'range': { setDataValue( memoryId, ctx, name, range(anyCommand.end, anyCommand.start, anyCommand.step), ); return success; } case 'concat-array': { setDataValue( memoryId, ctx, name, getPropertyList(memoryId, ctx, anyCommand.values).flatMap(asAnyArray), ); return success; } case 'mask-object': { const objectValue = getSupportedProperty(memoryId, ctx, anyCommand.value) || {}; if (typeof objectValue !== 'object') { return willFail({ message: `mask-object for path ${ anyCommand.value } expects an object but got ${typeof objectValue}`, coloredMessage: `mask-object for path ${coloration.path( anyCommand.value, )} expects an ${coloration.expected( 'object', )} but got ${coloration.actual(typeof objectValue)}`, }); } const masked = json_mask(objectValue, anyCommand.mask) as AnyDataValue; setDataValue(memoryId, ctx, name, masked); return success; } case 'template': { const templateContext = mergeTemplateContext({ memoryId, ctx, extra, command: anyCommand, }); const stringValue = getStringFromTemplate( anyCommand.template, templateContext, ); setDataValue(memoryId, ctx, name, stringValue); return success; } } return success; }; /** * Mostly used for testing purpose */ export const basicCommandsExecution = ( memoryId: string, ctx: Ctx, anyCommands: AnyCommand[], ): BasicExecution => { for (const anyCommand of anyCommands) { const result = basicCommandExecution(memoryId, ctx, anyCommand, {}); if (result.status === 'failure') { return result; } } return succeed(ctx); };