@atomic-ehr/fhirpath
Version:
A TypeScript implementation of FHIRPath
93 lines (77 loc) • 3.03 kB
text/typescript
import type { FunctionDefinition } from '../types';
import { Errors } from '../errors';
import type { FunctionEvaluator } from '../types';
import { box, unbox } from '../boxing';
export const evaluate: FunctionEvaluator = async (input, context, args, evaluator) => {
// contains() requires exactly 1 argument
if (args.length !== 1) {
throw Errors.wrongArgumentCount('contains', 1, args.length);
}
const substringExpr = args[0];
if (!substringExpr) {
throw Errors.argumentRequired('contains', 'substring argument');
}
// If input collection is empty, result is empty
if (input.length === 0) {
return { value: [], context };
}
// If input collection contains multiple items, signal an error
if (input.length > 1) {
throw Errors.invalidOperation('contains can only be applied to a single string');
}
// Input must be a string
const boxedInputValue = input[0];
if (!boxedInputValue) {
return { value: [], context };
}
const inputValue = unbox(boxedInputValue);
if (typeof inputValue !== 'string') {
// Non-string input returns empty
return { value: [], context };
}
// Evaluate the substring expression
const substringResult = await evaluator(substringExpr, input, context);
// If substring evaluation is empty, return empty
if (substringResult.value.length === 0) {
return { value: [], context };
}
// Substring must be a single string
if (substringResult.value.length > 1) {
throw Errors.invalidOperation('contains substring argument must evaluate to a single value');
}
const boxedSubstring = substringResult.value[0];
if (!boxedSubstring) {
return { value: [], context };
}
const substring = unbox(boxedSubstring);
if (typeof substring !== 'string') {
// Non-string substring returns empty
return { value: [], context };
}
// If substring is empty string, result is true
if (substring === '') {
return { value: [box(true, { type: 'Boolean', singleton: true })], context };
}
// Check if input string contains the substring
const result = inputValue.includes(substring);
return { value: [box(result, { type: 'Boolean', singleton: true })], context };
};
export const containsFunction: FunctionDefinition & { evaluate: FunctionEvaluator } = {
name: 'contains',
category: ['string'],
description: 'Returns true when the given substring is a substring of the input string. If substring is the empty string, the result is true. If the input collection is empty, the result is empty. If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.',
examples: [
"'abc'.contains('b')",
"'abc'.contains('bc')",
"'abc'.contains('d')"
],
signatures: [{
name: 'contains',
input: { type: 'String', singleton: true },
parameters: [
{ name: 'substring', type: { type: 'String', singleton: true } }
],
result: { type: 'Boolean', singleton: true }
}],
evaluate
};