applyConstraints method
Applies constraint annotations declared on a field or parameter.
Implementation
String applyConstraints(String schema, Element declaration, DartType type) {
var output = schema;
final isCollection =
type is InterfaceType && (type.isDartCoreList || type.isDartCoreSet);
final isSet = type is InterfaceType && type.isDartCoreSet;
final scalarType = isCollection && type.typeArguments.length == 1
? type.typeArguments.single
: type;
final isNumeric =
scalarType is InterfaceType &&
(_core(scalarType, 'int') ||
_core(scalarType, 'double') ||
_core(scalarType, 'num'));
final isString = scalarType is InterfaceType && _core(scalarType, 'String');
final itemConstraints = <String>[];
final setConstraints = <String>[];
var hasUniqueConstraint = false;
void addScalarConstraint(String constraint) {
if (isCollection) {
itemConstraints.add(constraint);
} else {
output = '$output$constraint';
}
}
void addCollectionConstraint(String constraint, {bool isUnique = false}) {
if (isSet) {
if (isUnique) hasUniqueConstraint = true;
setConstraints.add(constraint);
} else {
output = '$output$constraint';
}
}
for (final metadata in declaration.metadata.annotations) {
final value = metadata.computeConstantValue();
final valueType = value?.type;
if (value == null || valueType is! InterfaceType) continue;
if (!_rule.isAssignableFromType(valueType)) continue;
final rule = valueType.element.name!;
final annotation = '@$rule';
switch (rule) {
case 'IsOptional' || 'IsRequired' || 'IsNotNull':
break;
case 'HasMin':
_require(declaration, type, annotation, isNumeric, '@HasMinLength');
addScalarConstraint(
'.min(${_number(declaration, annotation, value, 'min')})',
);
case 'HasMax':
_require(declaration, type, annotation, isNumeric, '@HasMaxLength');
addScalarConstraint(
'.max(${_number(declaration, annotation, value, 'max')})',
);
case 'IsMultipleOf':
_require(declaration, type, annotation, isNumeric, 'numeric field');
addScalarConstraint(
'.multipleOf(${_number(declaration, annotation, value, 'divisor')})',
);
case 'IsPositive':
_require(declaration, type, annotation, isNumeric, 'numeric field');
addScalarConstraint('.positive()');
case 'IsNegative':
_require(declaration, type, annotation, isNumeric, 'numeric field');
addScalarConstraint('.negative()');
case 'HasMinLength':
_require(declaration, type, annotation, isString, '@HasMin');
addScalarConstraint('.minLength(${_int(value, 'length')})');
case 'HasMaxLength':
_require(declaration, type, annotation, isString, '@HasMax');
addScalarConstraint('.maxLength(${_int(value, 'length')})');
case 'HasMatch':
_require(declaration, type, annotation, isString, 'String field');
addScalarConstraint(
'.matches(${dartStringLiteral(value.getField('pattern')!.toStringValue()!)})',
);
case 'IsNotEmpty':
_require(
declaration,
type,
annotation,
isString || isCollection,
'String, List, or Set field',
);
if (isCollection) {
addCollectionConstraint('.notEmpty()');
} else {
output = '$output.notEmpty()';
}
case 'IsEmail':
_require(declaration, type, annotation, isString, 'String field');
addScalarConstraint('.email()');
case 'IsUrl':
_require(declaration, type, annotation, isString, 'String field');
addScalarConstraint('.url()');
case 'IsUri':
_require(declaration, type, annotation, isString, 'String field');
addScalarConstraint('.uri()');
case 'IsUuid':
_require(declaration, type, annotation, isString, 'String field');
addScalarConstraint('.uuid()');
case 'IsDate':
_require(declaration, type, annotation, isString, 'String field');
addScalarConstraint('.date()');
case 'IsDateTime':
_require(declaration, type, annotation, isString, 'String field');
addScalarConstraint('.datetime()');
case 'IsIp':
_require(declaration, type, annotation, isString, 'String field');
final version = value.getField('version')?.toIntValue();
if (version != null) {
if (version != 4 && version != 6) {
throw InvalidGenerationSource(
'${_qualifiedName(declaration)} has @IsIp(version: '
'$version); version must be 4 or 6.',
element: declaration,
);
}
addScalarConstraint('.ip(version: $version)');
} else {
addScalarConstraint('.ip()');
}
case 'HasMinItems':
_require(
declaration,
type,
annotation,
isCollection,
'List or Set field',
);
addCollectionConstraint('.minItems(${_int(value, 'count')})');
case 'HasMaxItems':
_require(
declaration,
type,
annotation,
isCollection,
'List or Set field',
);
addCollectionConstraint('.maxItems(${_int(value, 'count')})');
case 'HasUniqueItems':
_require(
declaration,
type,
annotation,
isCollection,
'List or Set field',
);
addCollectionConstraint('.unique()', isUnique: true);
default:
throw InvalidGenerationSource(
'${_qualifiedName(declaration)} has unsupported rule $annotation.',
element: declaration,
);
}
}
if (itemConstraints.isNotEmpty) {
output = _appendListItemConstraints(output, itemConstraints.join());
}
if (setConstraints.isNotEmpty) {
if (isSet && !hasUniqueConstraint) {
// A Set cannot retain duplicate values after decoding. Validate the
// boundary with the same cardinality before the codec runs.
setConstraints.add('.unique()');
}
output = _beforeOuterCodec(output, setConstraints.join());
}
return output;
}