- Unify test data for nonexistence of an object/string and collection. - Introduce more test through adding missing test data to existing tests. - Improve logic for checking absence of values to match tests. - Add missing tests for absent value validation. - Update documentation to include shared test functionality.
66 lines
2.8 KiB
TypeScript
66 lines
2.8 KiB
TypeScript
import { FunctionParameterCollection } from '@/application/Parser/Script/Compiler/Function/Parameter/FunctionParameterCollection';
|
|
import { IReadOnlyFunctionCallArgumentCollection } from '../../Function/Call/Argument/IFunctionCallArgumentCollection';
|
|
import { IReadOnlyFunctionParameterCollection } from '../../Function/Parameter/IFunctionParameterCollection';
|
|
import { FunctionCallArgumentCollection } from '../../Function/Call/Argument/FunctionCallArgumentCollection';
|
|
import { IExpression } from './IExpression';
|
|
import { ExpressionPosition } from './ExpressionPosition';
|
|
import { ExpressionEvaluationContext, IExpressionEvaluationContext } from './ExpressionEvaluationContext';
|
|
|
|
export type ExpressionEvaluator = (context: IExpressionEvaluationContext) => string;
|
|
export class Expression implements IExpression {
|
|
public readonly parameters: IReadOnlyFunctionParameterCollection;
|
|
|
|
constructor(
|
|
public readonly position: ExpressionPosition,
|
|
public readonly evaluator: ExpressionEvaluator,
|
|
parameters?: IReadOnlyFunctionParameterCollection,
|
|
) {
|
|
if (!position) {
|
|
throw new Error('missing position');
|
|
}
|
|
if (!evaluator) {
|
|
throw new Error('missing evaluator');
|
|
}
|
|
this.parameters = parameters ?? new FunctionParameterCollection();
|
|
}
|
|
|
|
public evaluate(context: IExpressionEvaluationContext): string {
|
|
if (!context) {
|
|
throw new Error('missing context');
|
|
}
|
|
validateThatAllRequiredParametersAreSatisfied(this.parameters, context.args);
|
|
const args = filterUnusedArguments(this.parameters, context.args);
|
|
const filteredContext = new ExpressionEvaluationContext(args, context.pipelineCompiler);
|
|
return this.evaluator(filteredContext);
|
|
}
|
|
}
|
|
|
|
function validateThatAllRequiredParametersAreSatisfied(
|
|
parameters: IReadOnlyFunctionParameterCollection,
|
|
args: IReadOnlyFunctionCallArgumentCollection,
|
|
) {
|
|
const requiredParameterNames = parameters
|
|
.all
|
|
.filter((parameter) => !parameter.isOptional)
|
|
.map((parameter) => parameter.name);
|
|
const missingParameterNames = requiredParameterNames
|
|
.filter((parameterName) => !args.hasArgument(parameterName));
|
|
if (missingParameterNames.length) {
|
|
throw new Error(
|
|
`argument values are provided for required parameters: "${missingParameterNames.join('", "')}"`,
|
|
);
|
|
}
|
|
}
|
|
|
|
function filterUnusedArguments(
|
|
parameters: IReadOnlyFunctionParameterCollection,
|
|
allFunctionArgs: IReadOnlyFunctionCallArgumentCollection,
|
|
): IReadOnlyFunctionCallArgumentCollection {
|
|
const specificCallArgs = new FunctionCallArgumentCollection();
|
|
parameters.all
|
|
.filter((parameter) => allFunctionArgs.hasArgument(parameter.name))
|
|
.map((parameter) => allFunctionArgs.getArgument(parameter.name))
|
|
.forEach((argument) => specificCallArgs.addArgument(argument));
|
|
return specificCallArgs;
|
|
}
|