import { z } from 'zod'; import { BuiltInParserRuleSignature } from '../../builtInParsers/internals/BuiltInParserRuleSignature.js'; import { ParserFunction } from '../definitions/ParserFunction.js'; import { builtInGenerationParserRuleSchema, builtInParserRuleSchema, builtInUtilityParserRuleSchema, } from '../../builtInParsers/builtInParserRule.js'; import { SpecifyError, specifyErrors } from '../../errors/index.js'; import { formatZodErrorMessages } from '../../utils/zod/index.js'; /** * Validate a rule and build a parser function that will throw the validation errors when executed * @internal * @param rule * @param objectPath - Used for error messages. The path from the rule name down to the sub-keys. * @param context */ export function validateRuleOrBuildErrorParserFunction( rule: unknown, objectPath: Array, context: { builtInParserKind: 'generation' | 'utility' | 'all'; }, ): | { isValid: true; rule: BuiltInParserRuleSignature; } | { isValid: false; parserFunctionWithError: ParserFunction; } { let schema: z.ZodSchema; switch (context.builtInParserKind) { case 'generation': schema = builtInGenerationParserRuleSchema; break; case 'utility': schema = builtInUtilityParserRuleSchema; break; case 'all': schema = builtInParserRuleSchema; break; default: throw new SpecifyError({ errorKey: specifyErrors.PARSERS_ENGINE_UNKNOWN_ERROR.errorKey, publicMessage: 'Design Error :: builtInParserKind must be "generation" | "utility" | "all"', }); } try { return { isValid: true, rule: schema.parse(rule, { path: objectPath, }) as BuiltInParserRuleSignature, }; } catch (error) { // We return a function that will throw the validation errors when executed const parserFunctionWithError: ParserFunction = async (_, toolbox) => { if (error instanceof z.ZodError) { const [first, ...rest] = formatZodErrorMessages(error); rest.forEach(message => { toolbox.populateMessage({ type: 'error', content: `Invalid rule: ${message}`, errorKey: specifyErrors.PARSERS_ENGINE_INVALID_RULE_CONFIGURATION.errorKey, }); }); throw new SpecifyError({ errorKey: specifyErrors.PARSERS_ENGINE_INVALID_RULE_CONFIGURATION.errorKey, publicMessage: `Invalid rule: ${first}`, }); /* v8 ignore next 3 */ } else { throw error; } }; return { isValid: false, parserFunctionWithError, }; } }