export function createValidator()

in src/vs/workbench/services/preferences/common/preferencesModels.ts [1025:1129]


export function createValidator(prop: IConfigurationPropertySchema): (value: any) => (string | null) {
	return value => {
		let exclusiveMax: number | undefined;
		let exclusiveMin: number | undefined;

		if (typeof prop.exclusiveMaximum === 'boolean') {
			exclusiveMax = prop.exclusiveMaximum ? prop.maximum : undefined;
		} else {
			exclusiveMax = prop.exclusiveMaximum;
		}

		if (typeof prop.exclusiveMinimum === 'boolean') {
			exclusiveMin = prop.exclusiveMinimum ? prop.minimum : undefined;
		} else {
			exclusiveMin = prop.exclusiveMinimum;
		}

		let patternRegex: RegExp | undefined;
		if (typeof prop.pattern === 'string') {
			patternRegex = new RegExp(prop.pattern);
		}

		const type = Array.isArray(prop.type) ? prop.type : [prop.type];
		const canBeType = (t: string) => type.indexOf(t) > -1;

		const isNullable = canBeType('null');
		const isNumeric = (canBeType('number') || canBeType('integer')) && (type.length === 1 || type.length === 2 && isNullable);
		const isIntegral = (canBeType('integer')) && (type.length === 1 || type.length === 2 && isNullable);

		type Validator<T> = { enabled: boolean, isValid: (value: T) => boolean; message: string };

		const numericValidations: Validator<number>[] = isNumeric ? [
			{
				enabled: exclusiveMax !== undefined && (prop.maximum === undefined || exclusiveMax <= prop.maximum),
				isValid: ((value: number) => value < exclusiveMax!),
				message: nls.localize('validations.exclusiveMax', "Value must be strictly less than {0}.", exclusiveMax)
			},
			{
				enabled: exclusiveMin !== undefined && (prop.minimum === undefined || exclusiveMin >= prop.minimum),
				isValid: ((value: number) => value > exclusiveMin!),
				message: nls.localize('validations.exclusiveMin', "Value must be strictly greater than {0}.", exclusiveMin)
			},

			{
				enabled: prop.maximum !== undefined && (exclusiveMax === undefined || exclusiveMax > prop.maximum),
				isValid: ((value: number) => value <= prop.maximum!),
				message: nls.localize('validations.max', "Value must be less than or equal to {0}.", prop.maximum)
			},
			{
				enabled: prop.minimum !== undefined && (exclusiveMin === undefined || exclusiveMin < prop.minimum),
				isValid: ((value: number) => value >= prop.minimum!),
				message: nls.localize('validations.min', "Value must be greater than or equal to {0}.", prop.minimum)
			},
			{
				enabled: prop.multipleOf !== undefined,
				isValid: ((value: number) => value % prop.multipleOf! === 0),
				message: nls.localize('validations.multipleOf', "Value must be a multiple of {0}.", prop.multipleOf)
			},
			{
				enabled: isIntegral,
				isValid: ((value: number) => value % 1 === 0),
				message: nls.localize('validations.expectedInteger', "Value must be an integer.")
			},
		].filter(validation => validation.enabled) : [];

		const stringValidations: Validator<string>[] = [
			{
				enabled: prop.maxLength !== undefined,
				isValid: ((value: { length: number; }) => value.length <= prop.maxLength!),
				message: nls.localize('validations.maxLength', "Value must be {0} or fewer characters long.", prop.maxLength)
			},
			{
				enabled: prop.minLength !== undefined,
				isValid: ((value: { length: number; }) => value.length >= prop.minLength!),
				message: nls.localize('validations.minLength', "Value must be {0} or more characters long.", prop.minLength)
			},
			{
				enabled: patternRegex !== undefined,
				isValid: ((value: string) => patternRegex!.test(value)),
				message: prop.patternErrorMessage || nls.localize('validations.regex', "Value must match regex `{0}`.", prop.pattern)
			},
		].filter(validation => validation.enabled);

		if (prop.type === 'string' && stringValidations.length === 0) { return null; }
		if (isNullable && value === '') { return ''; }

		const errors: string[] = [];

		if (isNumeric) {
			if (value === '' || isNaN(+value)) {
				errors.push(nls.localize('validations.expectedNumeric', "Value must be a number."));
			} else {
				errors.push(...numericValidations.filter(validator => !validator.isValid(+value)).map(validator => validator.message));
			}
		}

		if (prop.type === 'string') {
			errors.push(...stringValidations.filter(validator => !validator.isValid('' + value)).map(validator => validator.message));
		}
		if (errors.length) {
			return prop.errorMessage ? [prop.errorMessage, ...errors].join(' ') : errors.join(' ');
		}
		return '';
	};
}