@maplibre/maplibre-gl-style-spec
Version:
a specification for maplibre styles
168 lines (156 loc) • 5.47 kB
text/typescript
import {ValidationError} from '../error/validation_error';
import {validateExpression} from './validate_expression';
import {validateEnum} from './validate_enum';
import {getType} from '../util/get_type';
import {unbundle, deepUnbundle} from '../util/unbundle_jsonlint';
import {extendBy as extend} from '../util/extend';
import {
findMixedLegacyFilter,
getMixedFilterErrorMessage,
isExpressionFilter
} from '../feature_filter';
function getValueAtPath(value: any, path: number[]) {
let current = value;
for (const index of path) {
current = current[index];
}
return current;
}
export function validateFilter(options: any): ValidationError[] {
const value = deepUnbundle(options.value);
if (!isExpressionFilter(value)) {
return validateNonExpressionFilter(options);
}
const mixedLegacyDiagnostic = findMixedLegacyFilter(value);
if (mixedLegacyDiagnostic) {
const errorKey = `${options.key}${mixedLegacyDiagnostic.path.map((index) => `[${index}]`).join('')}`;
return [
new ValidationError(
errorKey,
getValueAtPath(options.value, mixedLegacyDiagnostic.path),
getMixedFilterErrorMessage(mixedLegacyDiagnostic.legacyFilter)
)
];
}
return validateExpression(
extend({}, options, {
expressionContext: 'filter',
valueSpec: {value: 'boolean'}
})
);
}
function validateNonExpressionFilter(options: any): ValidationError[] {
const value = options.value;
const key = options.key;
if (getType(value) !== 'array') {
return [new ValidationError(key, value, `array expected, ${getType(value)} found`)];
}
const styleSpec = options.styleSpec;
let type;
let errors: ValidationError[] = [];
if (value.length < 1) {
return [new ValidationError(key, value, 'filter array must have at least 1 element')];
}
errors = errors.concat(
validateEnum({
key: `${key}[0]`,
value: value[0],
valueSpec: styleSpec.filter_operator,
style: options.style,
styleSpec: options.styleSpec
})
);
switch (unbundle(value[0])) {
case '<':
case '<=':
case '>':
case '>=':
if (value.length >= 2 && unbundle(value[1]) === '$type') {
errors.push(
new ValidationError(
key,
value,
`"$type" cannot be use with operator "${value[0]}"`
)
);
}
/* falls through */
case '==':
case '!=':
if (value.length !== 3) {
errors.push(
new ValidationError(
key,
value,
`filter array for operator "${value[0]}" must have 3 elements`
)
);
}
/* falls through */
case 'in':
case '!in':
if (value.length >= 2) {
type = getType(value[1]);
if (type !== 'string') {
errors.push(
new ValidationError(`${key}[1]`, value[1], `string expected, ${type} found`)
);
}
}
for (let i = 2; i < value.length; i++) {
type = getType(value[i]);
if (unbundle(value[1]) === '$type') {
errors = errors.concat(
validateEnum({
key: `${key}[${i}]`,
value: value[i],
valueSpec: styleSpec.geometry_type,
style: options.style,
styleSpec: options.styleSpec
})
);
} else if (type !== 'string' && type !== 'number' && type !== 'boolean') {
errors.push(
new ValidationError(
`${key}[${i}]`,
value[i],
`string, number, or boolean expected, ${type} found`
)
);
}
}
break;
case 'any':
case 'all':
case 'none':
for (let i = 1; i < value.length; i++) {
errors = errors.concat(
validateNonExpressionFilter({
key: `${key}[${i}]`,
value: value[i],
style: options.style,
styleSpec: options.styleSpec
})
);
}
break;
case 'has':
case '!has':
type = getType(value[1]);
if (value.length !== 2) {
errors.push(
new ValidationError(
key,
value,
`filter array for "${value[0]}" operator must have 2 elements`
)
);
} else if (type !== 'string') {
errors.push(
new ValidationError(`${key}[1]`, value[1], `string expected, ${type} found`)
);
}
break;
}
return errors;
}