Skip to content

Commit 7547f20

Browse files
authored
fix: keep vine rules accross many validations (#3)
* feat: improve performance and validation architecture The changes show a significant refactoring of the validation architecture with the following key updates: - Convert rule storage from Queue to List for better performance - Remove field pooling optimization in favor of simpler code - Simplify rule parsing by removing positioned rules - Move nullable/optional rules to beginning of rules list - Streamline object/array schema validation flow - Update performance benchmark results in README - Fix various smaller validation issues This refactoring prioritizes code clarity and maintainability while retaining solid validation capabilities.
1 parent d4f29bc commit 7547f20

23 files changed

+185
-254
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ ensuring that data complies with an expected format before it is used, which red
1414
## 🛠 Key features
1515

1616
| Feature | Description |
17-
|---------------------------|--------------------------------------------------------------|
18-
| ✅ Type-Safe Validation | Define schemas with a fluent API and ensure data integrity |
17+
| ------------------------- | ------------------------------------------------------------ |
18+
| ✅ Type-Safe Validation | Define schemas with a fluent API and ensure data integrity |
1919
| 🧱 Rich Set of Validators | Strings, numbers, booleans, arrays, enums, objects, and more |
2020
| 🔄 Data Transformation | Trim, normalize, and transform values during validation |
2121
| 🚧 Null Safety | Full support for nullable and optional fields |
2222
| ⚙️ Composable | Compiled and reusable schemas |
23-
| ⚡ Fast Performance | ~ 29 000 000 ops/s |
23+
| ⚡ Fast Performance | ~ 3 000 000 ops/s |
2424
| 📦 Extremely small size | Package size `< 21kb` |
2525
| 🚀 OpenApi reporter | Export your schemas as OpenApi spec |
2626

@@ -86,7 +86,7 @@ void main() {
8686

8787
### OpenAPI reporter
8888

89-
Vine can generate an OpenAPI schema from your validation schemas.
89+
Vine can generate an OpenAPI schema from your validation schemas.
9090
This feature is useful when you want to document your API
9191

9292
```dart
@@ -111,4 +111,4 @@ print(reporter);
111111

112112
I would like to thank [Harminder Virk](https://github.com/thetutlage) for all his open-source work on Adonis.js and for
113113
his permission to
114-
reuse the name `Vine` for this package.
114+
reuse the name `Vine` for this package.

benchmark/array_object.dart

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
import 'package:vine/src/vine.dart';
22

33
void main() {
4-
final validator = vine.compile(vine.object({
5-
'contacts': vine.array(vine.object({
4+
final validator = vine.compile(vine.array(
5+
vine.object({
66
'type': vine.string(),
77
'value': vine.string().optional(),
8-
})),
9-
}));
8+
}),
9+
));
1010

11-
final payload = {
12-
'contacts': [
13-
{'type': 'email', 'value': '[email protected]'},
14-
{'type': 'phone', 'value': '12345678'},
15-
],
16-
};
11+
final payload = [
12+
{'type': 'email', 'value': '[email protected]'},
13+
];
1714

1815
final duration = Duration(seconds: 1);
1916
int iterationCount = 0;

benchmark/flat_object.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ void main() {
2020
iterationCount++;
2121
}
2222

23-
print('Flat Object : Processed $iterationCount iterations in ${duration.inSeconds} seconds');
23+
print(
24+
'Flat Object : Processed $iterationCount iterations in ${duration.inSeconds} seconds');
2425
}

lib/src/error_reporter.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ class SimpleErrorReporter implements VineErrorReporter {
3636
@override
3737
void report(String rule, List<String> keys, String message) {
3838
hasError = true;
39-
errors.add({'message': message, 'rule': rule, 'field': keys.join('.')});
39+
errors.add({
40+
'message': message,
41+
'rule': rule,
42+
if (keys.isNotEmpty) 'field': keys.join('.')
43+
});
4044
}
4145

4246
@override

lib/src/field.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ final class VineValidatorContext<T extends VineErrorReporter>
2626

2727
final class VineField implements VineFieldContext {
2828
@override
29-
final List<String> customKeys = [];
29+
List<String> customKeys = [];
3030

3131
@override
3232
String name;

lib/src/field_pool.dart

Lines changed: 0 additions & 23 deletions
This file was deleted.

lib/src/rule_parser.dart

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,21 @@
1-
import 'dart:collection';
2-
31
import 'package:vine/vine.dart';
42

53
abstract interface class RuleParserContract {
6-
Queue<VineRule> get rules;
7-
void addRule(VineRule rule, {bool positioned = false});
4+
List<VineRule> get rules;
85
}
96

107
class RuleParser implements RuleParserContract {
118
@override
12-
Queue<VineRule> rules;
9+
List<VineRule> rules;
1310

1411
bool isNullable = false;
1512
bool isOptional = false;
1613

1714
RuleParser(this.rules);
1815

19-
@override
20-
void addRule(VineRule rule, {bool positioned = false}) {
21-
if (positioned) {
22-
rules.addFirst(rule);
23-
return;
24-
}
25-
26-
rules.add(rule);
27-
}
28-
2916
VineFieldContext parse(VineValidationContext ctx, VineFieldContext field) {
30-
if (isNullable) {
31-
addRule(VineNullableRule(), positioned: true);
32-
}
33-
34-
if (isOptional) {
35-
addRule(VineOptionalRule(), positioned: true);
36-
}
37-
38-
while (rules.isNotEmpty) {
39-
final rule = rules.removeFirst();
17+
for (int i = 0; i < rules.length; i++) {
18+
final rule = rules[i];
4019
rule.handle(ctx, field);
4120

4221
if (!field.canBeContinue) break;

lib/src/rules/array_rule.dart

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import 'package:vine/src/contracts/rule.dart';
22
import 'package:vine/src/contracts/schema.dart';
33
import 'package:vine/src/contracts/vine.dart';
4-
import 'package:vine/src/field_pool.dart';
5-
import 'package:vine/src/rule_parser.dart';
4+
import 'package:vine/src/field.dart';
65

76
final class VineArrayRule implements VineRule {
87
final VineSchema schema;
@@ -11,27 +10,13 @@ final class VineArrayRule implements VineRule {
1110

1211
@override
1312
void handle(VineValidationContext ctx, VineFieldContext field) {
14-
final copy = field.customKeys;
15-
1613
if (field.value case List values) {
17-
final currentSchema = schema as RuleParser;
18-
final copyRules = currentSchema.rules.toList();
19-
2014
for (int i = 0; i < values.length; i++) {
21-
final currentField = VineFieldPool.acquire(field.name, values[i]);
22-
23-
currentSchema.rules.clear();
24-
currentSchema.rules.addAll(copyRules);
15+
final currentField = VineField(field.name, values[i])
16+
..customKeys = [...field.customKeys, i.toString()];
2517

26-
currentField.customKeys.add(i.toString());
2718
schema.parse(ctx, currentField);
28-
29-
currentField.customKeys
30-
..clear()
31-
..addAll(copy);
32-
3319
currentField.mutate([...field.value, currentField.value]);
34-
VineFieldPool.release(currentField);
3520
}
3621

3722
return;

lib/src/rules/object_rule.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import 'package:vine/src/contracts/rule.dart';
2-
import 'package:vine/src/contracts/schema.dart';
3-
import 'package:vine/src/contracts/vine.dart';
4-
import 'package:vine/src/field_pool.dart';
1+
import 'package:vine/vine.dart';
52

63
final class VineObjectRule implements VineRule {
74
final Map<String, VineSchema> payload;
@@ -28,7 +25,7 @@ final class VineObjectRule implements VineRule {
2825
final key = entry.key;
2926
final schema = entry.value;
3027

31-
final currentField = VineFieldPool.acquire(
28+
final currentField = VineField(
3229
key, fieldValue.containsKey(key) ? field.value[key] : MissingValue())
3330
..customKeys.addAll(List.of(field.customKeys, growable: false));
3431

@@ -49,7 +46,6 @@ final class VineObjectRule implements VineRule {
4946
resultMap[key] = currentField.value;
5047

5148
shouldBreak = !currentField.canBeContinue || ctx.errorReporter.hasError;
52-
VineFieldPool.release(currentField);
5349
}
5450

5551
final cleanedMap = {

lib/src/rules/union_rule.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'package:vine/src/contracts/rule.dart';
22
import 'package:vine/src/contracts/schema.dart';
33
import 'package:vine/src/contracts/vine.dart';
4-
import 'package:vine/src/field_pool.dart';
4+
import 'package:vine/vine.dart';
55

66
final class VineUnionRule implements VineRule {
77
final List<VineSchema> schemas;
@@ -12,7 +12,7 @@ final class VineUnionRule implements VineRule {
1212
void handle(VineValidationContext ctx, VineFieldContext field) {
1313
final List errors = [];
1414
field.customKeys.add(field.name);
15-
final currentField = VineFieldPool.acquire(field.name, field.value);
15+
final currentField = VineField(field.name, field.value);
1616

1717
currentField.isUnion = true;
1818
for (final schema in schemas) {
@@ -23,7 +23,6 @@ final class VineUnionRule implements VineRule {
2323
}
2424
}
2525
currentField.isUnion = false;
26-
VineFieldPool.release(currentField);
2726

2827
if (errors.length == schemas.length) {
2928
final error = ctx.errorReporter.format('union', field, null, {

0 commit comments

Comments
 (0)