diff --git a/README.md b/README.md index bae4f74..3c5b495 100755 --- a/README.md +++ b/README.md @@ -14,13 +14,13 @@ ensuring that data complies with an expected format before it is used, which red ## 🛠 Key features | Feature | Description | -|---------------------------|--------------------------------------------------------------| -| ✅ Type-Safe Validation | Define schemas with a fluent API and ensure data integrity | +| ------------------------- | ------------------------------------------------------------ | +| ✅ Type-Safe Validation | Define schemas with a fluent API and ensure data integrity | | 🧱 Rich Set of Validators | Strings, numbers, booleans, arrays, enums, objects, and more | | 🔄 Data Transformation | Trim, normalize, and transform values during validation | | 🚧 Null Safety | Full support for nullable and optional fields | | ⚙️ Composable | Compiled and reusable schemas | -| ⚡ Fast Performance | ~ 29 000 000 ops/s | +| ⚡ Fast Performance | ~ 3 000 000 ops/s | | 📦 Extremely small size | Package size `< 21kb` | | 🚀 OpenApi reporter | Export your schemas as OpenApi spec | @@ -86,7 +86,7 @@ void main() { ### OpenAPI reporter -Vine can generate an OpenAPI schema from your validation schemas. +Vine can generate an OpenAPI schema from your validation schemas. This feature is useful when you want to document your API ```dart @@ -111,4 +111,4 @@ print(reporter); I would like to thank [Harminder Virk](https://github.com/thetutlage) for all his open-source work on Adonis.js and for his permission to -reuse the name `Vine` for this package. +reuse the name `Vine` for this package. diff --git a/benchmark/array_object.dart b/benchmark/array_object.dart index 7e3b1fa..87e8dc3 100644 --- a/benchmark/array_object.dart +++ b/benchmark/array_object.dart @@ -1,19 +1,16 @@ import 'package:vine/src/vine.dart'; void main() { - final validator = vine.compile(vine.object({ - 'contacts': vine.array(vine.object({ + final validator = vine.compile(vine.array( + vine.object({ 'type': vine.string(), 'value': vine.string().optional(), - })), - })); + }), + )); - final payload = { - 'contacts': [ - {'type': 'email', 'value': 'foo@bar.com'}, - {'type': 'phone', 'value': '12345678'}, - ], - }; + final payload = [ + {'type': 'email', 'value': 'foo@bar.com'}, + ]; final duration = Duration(seconds: 1); int iterationCount = 0; diff --git a/benchmark/flat_object.dart b/benchmark/flat_object.dart index d35dbd9..baaa857 100644 --- a/benchmark/flat_object.dart +++ b/benchmark/flat_object.dart @@ -20,5 +20,6 @@ void main() { iterationCount++; } - print('Flat Object : Processed $iterationCount iterations in ${duration.inSeconds} seconds'); + print( + 'Flat Object : Processed $iterationCount iterations in ${duration.inSeconds} seconds'); } diff --git a/lib/src/error_reporter.dart b/lib/src/error_reporter.dart index 9a7f6d6..0d90b8a 100755 --- a/lib/src/error_reporter.dart +++ b/lib/src/error_reporter.dart @@ -36,7 +36,11 @@ class SimpleErrorReporter implements VineErrorReporter { @override void report(String rule, List keys, String message) { hasError = true; - errors.add({'message': message, 'rule': rule, 'field': keys.join('.')}); + errors.add({ + 'message': message, + 'rule': rule, + if (keys.isNotEmpty) 'field': keys.join('.') + }); } @override diff --git a/lib/src/field.dart b/lib/src/field.dart index 980b4b9..80bb169 100755 --- a/lib/src/field.dart +++ b/lib/src/field.dart @@ -26,7 +26,7 @@ final class VineValidatorContext final class VineField implements VineFieldContext { @override - final List customKeys = []; + List customKeys = []; @override String name; diff --git a/lib/src/field_pool.dart b/lib/src/field_pool.dart deleted file mode 100644 index e78e666..0000000 --- a/lib/src/field_pool.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'dart:collection'; - -import 'package:vine/vine.dart'; - -class VineFieldPool { - static final _pool = Queue(); - static final int _maxSize = 1000; - - static VineField acquire(String name, dynamic value) { - if (_pool.isEmpty) return VineField(name, value); - return _pool.removeFirst() - ..name = name - ..value = value - ..canBeContinue = true - ..customKeys.clear(); - } - - static void release(VineField field) { - if (_pool.length < _maxSize) { - _pool.add(field); - } - } -} diff --git a/lib/src/rule_parser.dart b/lib/src/rule_parser.dart index 010f32f..b22367e 100755 --- a/lib/src/rule_parser.dart +++ b/lib/src/rule_parser.dart @@ -1,42 +1,21 @@ -import 'dart:collection'; - import 'package:vine/vine.dart'; abstract interface class RuleParserContract { - Queue get rules; - void addRule(VineRule rule, {bool positioned = false}); + List get rules; } class RuleParser implements RuleParserContract { @override - Queue rules; + List rules; bool isNullable = false; bool isOptional = false; RuleParser(this.rules); - @override - void addRule(VineRule rule, {bool positioned = false}) { - if (positioned) { - rules.addFirst(rule); - return; - } - - rules.add(rule); - } - VineFieldContext parse(VineValidationContext ctx, VineFieldContext field) { - if (isNullable) { - addRule(VineNullableRule(), positioned: true); - } - - if (isOptional) { - addRule(VineOptionalRule(), positioned: true); - } - - while (rules.isNotEmpty) { - final rule = rules.removeFirst(); + for (int i = 0; i < rules.length; i++) { + final rule = rules[i]; rule.handle(ctx, field); if (!field.canBeContinue) break; diff --git a/lib/src/rules/array_rule.dart b/lib/src/rules/array_rule.dart index f4d35aa..ed67525 100755 --- a/lib/src/rules/array_rule.dart +++ b/lib/src/rules/array_rule.dart @@ -1,8 +1,7 @@ import 'package:vine/src/contracts/rule.dart'; import 'package:vine/src/contracts/schema.dart'; import 'package:vine/src/contracts/vine.dart'; -import 'package:vine/src/field_pool.dart'; -import 'package:vine/src/rule_parser.dart'; +import 'package:vine/src/field.dart'; final class VineArrayRule implements VineRule { final VineSchema schema; @@ -11,27 +10,13 @@ final class VineArrayRule implements VineRule { @override void handle(VineValidationContext ctx, VineFieldContext field) { - final copy = field.customKeys; - if (field.value case List values) { - final currentSchema = schema as RuleParser; - final copyRules = currentSchema.rules.toList(); - for (int i = 0; i < values.length; i++) { - final currentField = VineFieldPool.acquire(field.name, values[i]); - - currentSchema.rules.clear(); - currentSchema.rules.addAll(copyRules); + final currentField = VineField(field.name, values[i]) + ..customKeys = [...field.customKeys, i.toString()]; - currentField.customKeys.add(i.toString()); schema.parse(ctx, currentField); - - currentField.customKeys - ..clear() - ..addAll(copy); - currentField.mutate([...field.value, currentField.value]); - VineFieldPool.release(currentField); } return; diff --git a/lib/src/rules/object_rule.dart b/lib/src/rules/object_rule.dart index 9d924f6..86d4b83 100755 --- a/lib/src/rules/object_rule.dart +++ b/lib/src/rules/object_rule.dart @@ -1,7 +1,4 @@ -import 'package:vine/src/contracts/rule.dart'; -import 'package:vine/src/contracts/schema.dart'; -import 'package:vine/src/contracts/vine.dart'; -import 'package:vine/src/field_pool.dart'; +import 'package:vine/vine.dart'; final class VineObjectRule implements VineRule { final Map payload; @@ -28,7 +25,7 @@ final class VineObjectRule implements VineRule { final key = entry.key; final schema = entry.value; - final currentField = VineFieldPool.acquire( + final currentField = VineField( key, fieldValue.containsKey(key) ? field.value[key] : MissingValue()) ..customKeys.addAll(List.of(field.customKeys, growable: false)); @@ -49,7 +46,6 @@ final class VineObjectRule implements VineRule { resultMap[key] = currentField.value; shouldBreak = !currentField.canBeContinue || ctx.errorReporter.hasError; - VineFieldPool.release(currentField); } final cleanedMap = { diff --git a/lib/src/rules/union_rule.dart b/lib/src/rules/union_rule.dart index 2889192..b6def40 100755 --- a/lib/src/rules/union_rule.dart +++ b/lib/src/rules/union_rule.dart @@ -1,7 +1,7 @@ import 'package:vine/src/contracts/rule.dart'; import 'package:vine/src/contracts/schema.dart'; import 'package:vine/src/contracts/vine.dart'; -import 'package:vine/src/field_pool.dart'; +import 'package:vine/vine.dart'; final class VineUnionRule implements VineRule { final List schemas; @@ -12,7 +12,7 @@ final class VineUnionRule implements VineRule { void handle(VineValidationContext ctx, VineFieldContext field) { final List errors = []; field.customKeys.add(field.name); - final currentField = VineFieldPool.acquire(field.name, field.value); + final currentField = VineField(field.name, field.value); currentField.isUnion = true; for (final schema in schemas) { @@ -23,7 +23,6 @@ final class VineUnionRule implements VineRule { } } currentField.isUnion = false; - VineFieldPool.release(currentField); if (errors.length == schemas.length) { final error = ctx.errorReporter.format('union', field, null, { diff --git a/lib/src/schema/any_schema.dart b/lib/src/schema/any_schema.dart index ae5499d..c0b0455 100755 --- a/lib/src/schema/any_schema.dart +++ b/lib/src/schema/any_schema.dart @@ -1,5 +1,3 @@ -import 'dart:collection'; - import 'package:vine/src/contracts/schema.dart'; import 'package:vine/src/contracts/vine.dart'; import 'package:vine/src/rule_parser.dart'; @@ -12,43 +10,43 @@ final class VineAnySchema extends RuleParser implements VineAny { @override VineAny transform(Function(VineValidationContext, VineFieldContext) fn) { - super.addRule(VineTransformRule(fn)); + super.rules.add(VineTransformRule(fn)); return this; } @override VineAny requiredIfExist(List values) { - super.addRule(VineRequiredIfExistRule(values), positioned: true); + super.rules = [VineRequiredIfExistRule(values), ...super.rules]; return this; } @override VineAny requiredIfAnyExist(List values) { - super.addRule(VineRequiredIfAnyExistRule(values), positioned: true); + super.rules = [VineRequiredIfAnyExistRule(values), ...rules]; return this; } @override VineAny requiredIfMissing(List values) { - super.addRule(VineRequiredIfMissingRule(values), positioned: true); + super.rules = [VineRequiredIfMissingRule(values), ...rules]; return this; } @override VineAny requiredIfAnyMissing(List values) { - super.addRule(VineRequiredIfAnyMissingRule(values), positioned: true); + super.rules = [VineRequiredIfAnyMissingRule(values), ...rules]; return this; } @override VineAny nullable() { - super.isNullable = true; + super.rules = [VineNullableRule(), ...rules]; return this; } @override VineAny optional() { - super.isOptional = true; + super.rules = [VineOptionalRule(), ...rules]; return this; } @@ -60,7 +58,7 @@ final class VineAnySchema extends RuleParser implements VineAny { @override VineAny clone() { - return VineAnySchema(Queue.of(rules)); + return VineAnySchema([...rules]); } @override diff --git a/lib/src/schema/array_schema.dart b/lib/src/schema/array_schema.dart index 0ef127d..2e0cc32 100755 --- a/lib/src/schema/array_schema.dart +++ b/lib/src/schema/array_schema.dart @@ -12,73 +12,73 @@ final class VineArraySchema extends RuleParser implements VineArray { @override VineArray minLength(int value, {String? message}) { - super.addRule(VineArrayMinLengthRule(value, message)); + super.rules.add(VineArrayMinLengthRule(value, message)); return this; } @override VineArray maxLength(int value, {String? message}) { - super.addRule(VineArrayMaxLengthRule(value, message)); + super.rules.add(VineArrayMaxLengthRule(value, message)); return this; } @override VineArray fixedLength(int value, {String? message}) { - super.addRule(VineArrayFixedLengthRule(value, message)); + super.rules.add(VineArrayFixedLengthRule(value, message)); return this; } @override VineArray unique({String? message}) { - super.addRule(VineArrayUniqueRule(message)); + super.rules.add(VineArrayUniqueRule(message)); return this; } @override VineArray transform(Function(VineValidationContext, VineFieldContext) fn) { - super.addRule(VineTransformRule(fn)); + super.rules.add(VineTransformRule(fn)); return this; } @override VineArray requiredIfExist(List values) { - super.addRule(VineRequiredIfExistRule(values), positioned: true); + super.rules = [VineRequiredIfExistRule(values), ...super.rules]; return this; } @override VineArray requiredIfAnyExist(List values) { - super.addRule(VineRequiredIfAnyExistRule(values), positioned: true); + super.rules = [VineRequiredIfAnyExistRule(values), ...rules]; return this; } @override VineArray requiredIfMissing(List values) { - super.addRule(VineRequiredIfMissingRule(values), positioned: true); + super.rules = [VineRequiredIfMissingRule(values), ...rules]; return this; } @override VineArray requiredIfAnyMissing(List values) { - super.addRule(VineRequiredIfAnyMissingRule(values), positioned: true); + super.rules = [VineRequiredIfAnyMissingRule(values), ...rules]; return this; } @override VineArray nullable() { - super.isNullable = true; + super.rules = [VineNullableRule(), ...rules]; return this; } @override VineArray optional() { - super.isOptional = true; + super.rules = [VineOptionalRule(), ...rules]; return this; } @override VineArray clone() { - return VineArraySchema(Queue.of(rules)); + return VineArraySchema([...rules]); } int? _getRuleValue() { diff --git a/lib/src/schema/boolean_schema.dart b/lib/src/schema/boolean_schema.dart index 634deaa..f92e35f 100755 --- a/lib/src/schema/boolean_schema.dart +++ b/lib/src/schema/boolean_schema.dart @@ -1,5 +1,3 @@ -import 'dart:collection'; - import 'package:vine/src/contracts/schema.dart'; import 'package:vine/src/contracts/vine.dart'; import 'package:vine/src/rule_parser.dart'; @@ -12,43 +10,43 @@ final class VineBooleanSchema extends RuleParser implements VineBoolean { @override VineBoolean transform(Function(VineValidationContext, VineFieldContext) fn) { - super.addRule(VineTransformRule(fn)); + super.rules.add(VineTransformRule(fn)); return this; } @override VineBoolean requiredIfExist(List values) { - super.addRule(VineRequiredIfExistRule(values), positioned: true); + super.rules = [VineRequiredIfExistRule(values), ...super.rules]; return this; } @override VineBoolean requiredIfAnyExist(List values) { - super.addRule(VineRequiredIfAnyExistRule(values), positioned: true); + super.rules = [VineRequiredIfAnyExistRule(values), ...rules]; return this; } @override VineBoolean requiredIfMissing(List values) { - super.addRule(VineRequiredIfMissingRule(values), positioned: true); + super.rules = [VineRequiredIfMissingRule(values), ...rules]; return this; } @override VineBoolean requiredIfAnyMissing(List values) { - super.addRule(VineRequiredIfAnyMissingRule(values), positioned: true); + super.rules = [VineRequiredIfAnyMissingRule(values), ...rules]; return this; } @override - VineBoolean nullable() { - super.isNullable = true; + VineBoolean optional() { + super.rules = [VineOptionalRule(), ...rules]; return this; } @override - VineBoolean optional() { - super.isOptional = true; + VineBoolean nullable() { + super.rules = [VineNullableRule(), ...rules]; return this; } @@ -60,7 +58,7 @@ final class VineBooleanSchema extends RuleParser implements VineBoolean { @override VineBoolean clone() { - return VineBooleanSchema(Queue.of(rules)); + return VineBooleanSchema([...rules]); } @override diff --git a/lib/src/schema/date_schema.dart b/lib/src/schema/date_schema.dart index 6d63e52..72fb1f5 100755 --- a/lib/src/schema/date_schema.dart +++ b/lib/src/schema/date_schema.dart @@ -1,5 +1,3 @@ -import 'dart:collection'; - import 'package:vine/src/contracts/schema.dart'; import 'package:vine/src/contracts/vine.dart'; import 'package:vine/src/rule_parser.dart'; @@ -13,79 +11,79 @@ final class VineDateSchema extends RuleParser implements VineDate { @override VineDate before(DateTime value, {String? message}) { - super.addRule(VineDateBeforeRule(value, message)); + super.rules.add(VineDateBeforeRule(value, message)); return this; } @override VineDate after(DateTime value, {String? message}) { - super.addRule(VineDateAfterRule(value, message)); + super.rules.add(VineDateAfterRule(value, message)); return this; } @override VineDate between(DateTime min, DateTime max, {String? message}) { - super.addRule(VineDateBetweenRule(min, max, message)); + super.rules.add(VineDateBetweenRule(min, max, message)); return this; } @override VineDate beforeField(String target, {String? message}) { - super.addRule(VineDateBeforeFieldRule(target, message)); + super.rules.add(VineDateBeforeFieldRule(target, message)); return this; } @override VineDate afterField(String target, {String? message}) { - super.addRule(VineDateAfterFieldRule(target, message)); + super.rules.add(VineDateAfterFieldRule(target, message)); return this; } @override VineDate betweenFields(String start, String end, {String? message}) { - super.addRule(VineDateBetweenFieldRule(start, end, message)); + super.rules.add(VineDateBetweenFieldRule(start, end, message)); return this; } @override VineDate transform(Function(VineValidationContext, VineFieldContext) fn) { - super.addRule(VineTransformRule(fn)); + super.rules.add(VineTransformRule(fn)); return this; } @override VineDate requiredIfExist(List values) { - super.addRule(VineRequiredIfExistRule(values), positioned: true); + super.rules = [VineRequiredIfExistRule(values), ...super.rules]; return this; } @override VineDate requiredIfAnyExist(List values) { - super.addRule(VineRequiredIfAnyExistRule(values), positioned: true); + super.rules = [VineRequiredIfAnyExistRule(values), ...rules]; return this; } @override VineDate requiredIfMissing(List values) { - super.addRule(VineRequiredIfMissingRule(values), positioned: true); + super.rules = [VineRequiredIfMissingRule(values), ...rules]; return this; } @override VineDate requiredIfAnyMissing(List values) { - super.addRule(VineRequiredIfAnyMissingRule(values), positioned: true); + super.rules = [VineRequiredIfAnyMissingRule(values), ...rules]; return this; } @override VineDate nullable() { - super.isNullable = true; + super.rules = [VineNullableRule(), ...rules]; return this; } @override VineDate optional() { - super.isOptional = true; + super.rules = [VineOptionalRule(), ...rules]; return this; } @@ -97,7 +95,7 @@ final class VineDateSchema extends RuleParser implements VineDate { @override VineDate clone() { - return VineDateSchema(Queue.of(rules)); + return VineDateSchema([...rules]); } @override diff --git a/lib/src/schema/enum_schema.dart b/lib/src/schema/enum_schema.dart index f7f5d6c..b1bd3e2 100755 --- a/lib/src/schema/enum_schema.dart +++ b/lib/src/schema/enum_schema.dart @@ -1,5 +1,3 @@ -import 'dart:collection'; - import 'package:vine/vine.dart'; final class VineEnumSchema extends RuleParser @@ -10,44 +8,44 @@ final class VineEnumSchema extends RuleParser VineEnumSchema(super._rules, this._source); @override - VineEnum requiredIfExist(List values) { - super.addRule(VineRequiredIfExistRule(values), positioned: true); + VineEnum transform(Function(VineValidationContext, VineFieldContext) fn) { + super.rules.add(VineTransformRule(fn)); return this; } @override - VineEnum requiredIfAnyExist(List values) { - super.addRule(VineRequiredIfAnyExistRule(values), positioned: true); + VineEnum requiredIfExist(List values) { + super.rules = [VineRequiredIfExistRule(values), ...super.rules]; return this; } @override - VineEnum requiredIfMissing(List values) { - super.addRule(VineRequiredIfMissingRule(values), positioned: true); + VineEnum requiredIfAnyExist(List values) { + super.rules = [VineRequiredIfAnyExistRule(values), ...rules]; return this; } @override - VineEnum requiredIfAnyMissing(List values) { - super.addRule(VineRequiredIfAnyMissingRule(values), positioned: true); + VineEnum requiredIfMissing(List values) { + super.rules = [VineRequiredIfMissingRule(values), ...rules]; return this; } @override - VineEnum transform(Function(VineValidationContext, VineFieldContext) fn) { - super.addRule(VineTransformRule(fn)); + VineEnum requiredIfAnyMissing(List values) { + super.rules = [VineRequiredIfAnyMissingRule(values), ...rules]; return this; } @override VineEnum nullable() { - super.isNullable = true; + super.rules = [VineNullableRule(), ...rules]; return this; } @override VineEnum optional() { - super.isOptional = true; + super.rules = [VineOptionalRule(), ...rules]; return this; } @@ -59,7 +57,7 @@ final class VineEnumSchema extends RuleParser @override VineEnum clone() { - return VineEnumSchema(Queue.of(rules), _source.toList()); + return VineEnumSchema([...rules], _source.toList()); } @override diff --git a/lib/src/schema/number_schema.dart b/lib/src/schema/number_schema.dart index 23131a0..288a2a5 100755 --- a/lib/src/schema/number_schema.dart +++ b/lib/src/schema/number_schema.dart @@ -13,85 +13,85 @@ final class VineNumberSchema extends RuleParser implements VineNumber { @override VineNumber range(List values, {String? message}) { - super.addRule(VineRangeRule(values, message)); + super.rules.add(VineRangeRule(values, message)); return this; } @override VineNumber min(num value, {String? message}) { - super.addRule(VineMinRule(value, message)); + super.rules.add(VineMinRule(value, message)); return this; } @override VineNumber max(num value, {String? message}) { - super.addRule(VineMaxRule(value, message)); + super.rules.add(VineMaxRule(value, message)); return this; } @override VineNumber negative({String? message}) { - super.addRule(VineNegativeRule(message)); + super.rules.add(VineNegativeRule(message)); return this; } @override VineNumber positive({String? message}) { - super.addRule(VinePositiveRule(message)); + super.rules.add(VinePositiveRule(message)); return this; } @override VineNumber double({String? message}) { - super.addRule(VineDoubleRule(message)); + super.rules.add(VineDoubleRule(message)); return this; } @override VineNumber integer({String? message}) { - super.addRule(VineIntegerRule(message)); + super.rules.add(VineIntegerRule(message)); return this; } @override VineNumber requiredIfExist(List values) { - super.addRule(VineRequiredIfExistRule(values), positioned: true); + super.rules = [VineRequiredIfExistRule(values), ...super.rules]; return this; } @override VineNumber requiredIfAnyExist(List values) { - super.addRule(VineRequiredIfAnyExistRule(values), positioned: true); + super.rules = [VineRequiredIfAnyExistRule(values), ...rules]; return this; } @override VineNumber requiredIfMissing(List values) { - super.addRule(VineRequiredIfMissingRule(values), positioned: true); + super.rules = [VineRequiredIfMissingRule(values), ...rules]; return this; } @override VineNumber requiredIfAnyMissing(List values) { - super.addRule(VineRequiredIfAnyMissingRule(values), positioned: true); + super.rules = [VineRequiredIfAnyMissingRule(values), ...rules]; return this; } @override VineNumber transform(Function(VineValidationContext, VineFieldContext) fn) { - super.addRule(VineTransformRule(fn)); + super.rules.add(VineTransformRule(fn)); return this; } @override VineNumber nullable() { - super.isNullable = true; + super.rules = [VineNullableRule(), ...rules]; return this; } @override VineNumber optional() { - super.isOptional = true; + super.rules = [VineOptionalRule(), ...rules]; return this; } @@ -103,7 +103,7 @@ final class VineNumberSchema extends RuleParser implements VineNumber { @override VineNumber clone() { - return VineNumberSchema(Queue.of(rules)); + return VineNumberSchema([...rules]); } @override diff --git a/lib/src/schema/object/group_schema.dart b/lib/src/schema/object/group_schema.dart index 6bfac83..06f4e42 100644 --- a/lib/src/schema/object/group_schema.dart +++ b/lib/src/schema/object/group_schema.dart @@ -1,5 +1,3 @@ -import 'dart:collection'; - import 'package:vine/src/rules/group_object_rule.dart'; import 'package:vine/vine.dart'; @@ -9,19 +7,19 @@ final class VineGroupSchema extends RuleParser implements VineGroup { @override VineGroup when(bool Function(Map data) fn, Map object) { - super.addRule(VineObjectGroupRule(fn, object)); + super.rules.add(VineObjectGroupRule(fn, object)); return this; } @override VineGroup otherwise(Function(VineValidationContext, VineFieldContext) fn) { - super.addRule(VineObjectOtherwiseRule(fn)); + super.rules.add(VineObjectOtherwiseRule(fn)); return this; } @override VineGroup clone() { - return VineGroupSchema(Queue.of(rules)); + return VineGroupSchema([...rules]); } @override diff --git a/lib/src/schema/object/object_schema.dart b/lib/src/schema/object/object_schema.dart index eeadda5..cc556f8 100755 --- a/lib/src/schema/object/object_schema.dart +++ b/lib/src/schema/object/object_schema.dart @@ -4,6 +4,7 @@ import 'package:vine/src/contracts/schema.dart'; import 'package:vine/src/contracts/vine.dart'; import 'package:vine/src/rule_parser.dart'; import 'package:vine/src/rules/basic_rule.dart'; +import 'package:vine/vine.dart'; final class VineObjectSchema extends RuleParser implements VineObject { final Map _properties; @@ -21,31 +22,31 @@ final class VineObjectSchema extends RuleParser implements VineObject { @override VineObject transform(Function(VineValidationContext, VineFieldContext) fn) { - super.addRule(VineTransformRule(fn)); + super.rules.add(VineTransformRule(fn)); return this; } @override VineObject requiredIfExist(List values) { - super.addRule(VineRequiredIfExistRule(values), positioned: true); + super.rules = [VineRequiredIfExistRule(values), ...super.rules]; return this; } @override VineObject requiredIfAnyExist(List values) { - super.addRule(VineRequiredIfAnyExistRule(values), positioned: true); + super.rules = [VineRequiredIfAnyExistRule(values), ...rules]; return this; } @override VineObject requiredIfMissing(List values) { - super.addRule(VineRequiredIfMissingRule(values), positioned: true); + super.rules = [VineRequiredIfMissingRule(values), ...rules]; return this; } @override VineObject requiredIfAnyMissing(List values) { - super.addRule(VineRequiredIfAnyMissingRule(values), positioned: true); + super.rules = [VineRequiredIfAnyMissingRule(values), ...rules]; return this; } @@ -63,7 +64,12 @@ final class VineObjectSchema extends RuleParser implements VineObject { @override VineObject clone() { - return VineObjectSchema({..._properties}, Queue.of(rules)); + final Map props = {}; + for (final entry in _properties.entries) { + props[entry.key] = entry.value.clone(); + } + + return VineObjectSchema(props, [...rules]); } @override diff --git a/lib/src/schema/string_schema.dart b/lib/src/schema/string_schema.dart index 235a19c..e21e085 100755 --- a/lib/src/schema/string_schema.dart +++ b/lib/src/schema/string_schema.dart @@ -1,5 +1,3 @@ -import 'dart:collection'; - import 'package:vine/vine.dart'; final class VineStringSchema extends RuleParser implements VineString { @@ -9,37 +7,37 @@ final class VineStringSchema extends RuleParser implements VineString { @override VineString minLength(int value, {String? message}) { - super.addRule(VineMinLengthRule(value, message)); + super.rules.add(VineMinLengthRule(value, message)); return this; } @override VineString maxLength(int value, {String? message}) { - super.addRule(VineMaxLengthRule(value, message)); + super.rules.add(VineMaxLengthRule(value, message)); return this; } @override VineString fixedLength(int value, {String? message}) { - super.addRule(VineFixedLengthRule(value, message)); + super.rules.add(VineFixedLengthRule(value, message)); return this; } @override VineString email({String? message}) { - super.addRule(VineEmailRule(message)); + super.rules.add(VineEmailRule(message)); return this; } @override VineString phone({RegExp? match, String? message}) { - super.addRule(VinePhoneRule(match, message)); + super.rules.add(VinePhoneRule(match, message)); return this; } @override VineString ipAddress({IpAddressVersion? version, String? message}) { - super.addRule(VineIpAddressRule(version, message)); + super.rules.add(VineIpAddressRule(version, message)); return this; } @@ -50,153 +48,153 @@ final class VineStringSchema extends RuleParser implements VineString { bool requireProtocol = false, bool allowUnderscores = false, String? message}) { - super.addRule(VineUrlRule( + super.rules.add(VineUrlRule( protocols, requireTld, requireProtocol, allowUnderscores, message)); return this; } @override VineString alpha({String? message}) { - super.addRule(VineAlphaRule(message)); + super.rules.add(VineAlphaRule(message)); return this; } @override VineString alphaNumeric({String? message}) { - super.addRule(VineAlphaNumericRule(message)); + super.rules.add(VineAlphaNumericRule(message)); return this; } @override VineString startsWith(String value, {String? message}) { - super.addRule(VineStartWithRule(value, message)); + super.rules.add(VineStartWithRule(value, message)); return this; } @override VineString endsWith(String value, {String? message}) { - super.addRule(VineEndWithRule(value, message)); + super.rules.add(VineEndWithRule(value, message)); return this; } @override VineString confirmed( {String? property, bool include = false, String? message}) { - super.addRule(VineConfirmedRule(property, include, message)); + super.rules.add(VineConfirmedRule(property, include, message)); return this; } @override VineString regex(RegExp expression, {String? message}) { - super.addRule(VineRegexRule(expression, message)); + super.rules.add(VineRegexRule(expression, message)); return this; } @override VineString trim() { - super.addRule(VineTrimRule()); + super.rules.add(VineTrimRule()); return this; } @override VineString normalizeEmail({bool lowercase = true}) { - super.addRule(VineNormalizeEmailRule(lowercase)); + super.rules.add(VineNormalizeEmailRule(lowercase)); return this; } @override VineString toUpperCase() { - super.addRule(VineUpperCaseRule()); + super.rules.add(VineUpperCaseRule()); return this; } @override VineString toLowerCase() { - super.addRule(VineLowerCaseRule()); + super.rules.add(VineLowerCaseRule()); return this; } @override VineString toCamelCase() { - super.addRule(VineToCamelCaseRule()); + super.rules.add(VineToCamelCaseRule()); return this; } @override VineString uuid({UuidVersion? version, String? message}) { - super.addRule(VineUuidRule(version, message)); + super.rules.add(VineUuidRule(version, message)); return this; } @override VineString isCreditCard({String? message}) { - super.addRule(VineCreditCardRule(message)); + super.rules.add(VineCreditCardRule(message)); return this; } @override VineString sameAs(String value, {String? message}) { - super.addRule(VineSameAsRule(value, message)); + super.rules.add(VineSameAsRule(value, message)); return this; } @override VineString notSameAs(String value, {String? message}) { - super.addRule(VineNotSameAsRule(value, message)); + super.rules.add(VineNotSameAsRule(value, message)); return this; } @override VineString inList(List values, {String? message}) { - super.addRule(VineInListRule(values, message)); + super.rules.add(VineInListRule(values, message)); return this; } @override VineString notInList(List values, {String? message}) { - super.addRule(VineNotInListRule(values, message)); + super.rules.add(VineNotInListRule(values, message)); return this; } @override VineString requiredIfExist(List values) { - super.addRule(VineRequiredIfExistRule(values), positioned: true); + super.rules = [VineRequiredIfExistRule(values), ...super.rules]; return this; } @override VineString requiredIfAnyExist(List values) { - super.addRule(VineRequiredIfAnyExistRule(values), positioned: true); + super.rules = [VineRequiredIfAnyExistRule(values), ...rules]; return this; } @override VineString requiredIfMissing(List values) { - super.addRule(VineRequiredIfMissingRule(values), positioned: true); + super.rules = [VineRequiredIfMissingRule(values), ...rules]; return this; } @override VineString requiredIfAnyMissing(List values) { - super.addRule(VineRequiredIfAnyMissingRule(values), positioned: true); + super.rules = [VineRequiredIfAnyMissingRule(values), ...rules]; return this; } @override VineString transform(Function(VineValidationContext, VineFieldContext) fn) { - super.addRule(VineTransformRule(fn)); + super.rules.add(VineTransformRule(fn)); return this; } @override VineString nullable() { - super.isNullable = true; + super.rules = [VineNullableRule(), ...rules]; return this; } @override VineString optional() { - super.isOptional = true; + super.rules = [VineOptionalRule(), ...rules]; return this; } @@ -208,7 +206,7 @@ final class VineStringSchema extends RuleParser implements VineString { @override VineString clone() { - return VineStringSchema(Queue.of(rules)); + return VineStringSchema([...rules]); } @override diff --git a/lib/src/schema/union_schema.dart b/lib/src/schema/union_schema.dart index 8799a6c..cfe912a 100644 --- a/lib/src/schema/union_schema.dart +++ b/lib/src/schema/union_schema.dart @@ -1,5 +1,3 @@ -import 'dart:collection'; - import 'package:vine/vine.dart'; final class VineUnionSchema extends RuleParser implements VineUnion { @@ -10,43 +8,43 @@ final class VineUnionSchema extends RuleParser implements VineUnion { @override VineUnion requiredIfExist(List values) { - super.addRule(VineRequiredIfExistRule(values), positioned: true); + super.rules = [VineRequiredIfExistRule(values), ...super.rules]; return this; } @override VineUnion requiredIfAnyExist(List values) { - super.addRule(VineRequiredIfAnyExistRule(values), positioned: true); + super.rules = [VineRequiredIfAnyExistRule(values), ...rules]; return this; } @override VineUnion requiredIfMissing(List values) { - super.addRule(VineRequiredIfMissingRule(values), positioned: true); + super.rules = [VineRequiredIfMissingRule(values), ...rules]; return this; } @override VineUnion requiredIfAnyMissing(List values) { - super.addRule(VineRequiredIfAnyMissingRule(values), positioned: true); + super.rules = [VineRequiredIfAnyMissingRule(values), ...rules]; return this; } @override VineUnion transform(Function(VineValidationContext, VineFieldContext) fn) { - super.addRule(VineTransformRule(fn)); + super.rules.add(VineTransformRule(fn)); return this; } @override VineUnion nullable() { - super.isNullable = true; + super.rules = [VineNullableRule(), ...rules]; return this; } @override VineUnion optional() { - super.isOptional = true; + super.rules = [VineOptionalRule(), ...rules]; return this; } @@ -58,7 +56,7 @@ final class VineUnionSchema extends RuleParser implements VineUnion { @override VineUnion clone() { - return VineUnionSchema(Queue.of(rules), _schemas.toList()); + return VineUnionSchema([...rules], _schemas.toList()); } @override diff --git a/lib/src/vine.dart b/lib/src/vine.dart index 28d5f17..e4db8cd 100644 --- a/lib/src/vine.dart +++ b/lib/src/vine.dart @@ -1,5 +1,3 @@ -import 'dart:collection'; - import 'package:vine/src/contracts/rule.dart'; import 'package:vine/src/contracts/schema.dart'; import 'package:vine/src/contracts/vine.dart'; @@ -32,13 +30,13 @@ final class Vine { SimpleErrorReporter.new; VineObject object(Map payload, {String? message}) { - final Queue rules = Queue(); + final List rules = []; rules.add(VineObjectRule(payload, message)); return VineObjectSchema(payload, rules); } VineGroup group(Function(VineGroupSchema) builder) { - final Queue rules = Queue(); + final List rules = []; final group = VineGroupSchema(rules); builder(group); @@ -46,56 +44,56 @@ final class Vine { } VineString string({String? message}) { - final Queue rules = Queue(); + final List rules = []; rules.add(VineStringRule(message)); return VineStringSchema(rules); } VineNumber number({String? message}) { - final Queue rules = Queue(); + final List rules = []; rules.add(VineNumberRule(message)); return VineNumberSchema(rules); } VineBoolean boolean({bool includeLiteral = false, String? message}) { - final Queue rules = Queue(); + final List rules = []; rules.add(VineBooleanRule(includeLiteral, message)); return VineBooleanSchema(rules); } VineAny any() { - final Queue rules = Queue(); + final List rules = []; rules.add(VineAnyRule()); return VineAnySchema(rules); } VineEnum enumerate(List source) { - final Queue rules = Queue(); + final List rules = []; rules.add(VineEnumRule(source)); return VineEnumSchema(rules, source); } VineArray array(VineSchema schema) { - final Queue rules = Queue(); + final List rules = []; rules.add(VineArrayRule(schema)); return VineArraySchema(rules); } VineUnion union(List schemas) { - final Queue rules = Queue(); + final List rules = []; rules.add(VineUnionRule(schemas)); return VineUnionSchema(rules, schemas); } VineDate date({String? message}) { - final Queue rules = Queue(); + final List rules = []; rules.add(VineDateRule(message)); return VineDateSchema(rules); @@ -144,6 +142,7 @@ final class Validator implements VineValidatorContract { T validate(dynamic data) { final validatorContext = VineValidatorContext(reporter, data); final field = VineField('', data); + _schema.parse(validatorContext, field); if (reporter.hasError) { diff --git a/test/rules/number_test.dart b/test/rules/number_test.dart index fe1d4fe..ab17a26 100755 --- a/test/rules/number_test.dart +++ b/test/rules/number_test.dart @@ -301,5 +301,21 @@ void main() { expect(() => validator.validate({'age': 'not a number'}), throwsA(isA())); }); + + test('validate many times', () { + final validator = vine.compile(vine.object({ + 'toto': vine.number().min(18).optional(), + })); + + final payload = {'toto': 25}; + + expect(() => validator.validate(payload), returnsNormally); + expect(() { + return validator.validate({ + ...payload, + 'toto': 17, + }); + }, throwsA(isA())); + }); }); } diff --git a/test/test.test.dart b/test/test.test.dart deleted file mode 100644 index 25e3ed8..0000000 --- a/test/test.test.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:test/expect.dart'; -import 'package:test/scaffolding.dart'; -import 'package:vine/src/vine.dart'; - -import 'rules/enum_test.dart'; - -void main() { - test('test', () { - final validator = vine.compile( - vine.object({'value': vine.enumerate(MyEnum.values).optional()})); - - expect(() => validator.validate({}), returnsNormally); - }); -}