Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand Down Expand Up @@ -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
Expand All @@ -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.
17 changes: 7 additions & 10 deletions benchmark/array_object.dart
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
3 changes: 2 additions & 1 deletion benchmark/flat_object.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
6 changes: 5 additions & 1 deletion lib/src/error_reporter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ class SimpleErrorReporter implements VineErrorReporter {
@override
void report(String rule, List<String> 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
Expand Down
2 changes: 1 addition & 1 deletion lib/src/field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ final class VineValidatorContext<T extends VineErrorReporter>

final class VineField implements VineFieldContext {
@override
final List<String> customKeys = [];
List<String> customKeys = [];

@override
String name;
Expand Down
23 changes: 0 additions & 23 deletions lib/src/field_pool.dart

This file was deleted.

29 changes: 4 additions & 25 deletions lib/src/rule_parser.dart
Original file line number Diff line number Diff line change
@@ -1,42 +1,21 @@
import 'dart:collection';

import 'package:vine/vine.dart';

abstract interface class RuleParserContract {
Queue<VineRule> get rules;
void addRule(VineRule rule, {bool positioned = false});
List<VineRule> get rules;
}

class RuleParser implements RuleParserContract {
@override
Queue<VineRule> rules;
List<VineRule> 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;
Expand Down
21 changes: 3 additions & 18 deletions lib/src/rules/array_rule.dart
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down
8 changes: 2 additions & 6 deletions lib/src/rules/object_rule.dart
Original file line number Diff line number Diff line change
@@ -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<String, VineSchema> payload;
Expand All @@ -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));

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

shouldBreak = !currentField.canBeContinue || ctx.errorReporter.hasError;
VineFieldPool.release(currentField);
}

final cleanedMap = {
Expand Down
5 changes: 2 additions & 3 deletions lib/src/rules/union_rule.dart
Original file line number Diff line number Diff line change
@@ -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<VineSchema> schemas;
Expand All @@ -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) {
Expand All @@ -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, {
Expand Down
18 changes: 8 additions & 10 deletions lib/src/schema/any_schema.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<String> values) {
super.addRule(VineRequiredIfExistRule(values), positioned: true);
super.rules = [VineRequiredIfExistRule(values), ...super.rules];
return this;
}

@override
VineAny requiredIfAnyExist(List<String> values) {
super.addRule(VineRequiredIfAnyExistRule(values), positioned: true);
super.rules = [VineRequiredIfAnyExistRule(values), ...rules];
return this;
}

@override
VineAny requiredIfMissing(List<String> values) {
super.addRule(VineRequiredIfMissingRule(values), positioned: true);
super.rules = [VineRequiredIfMissingRule(values), ...rules];
return this;
}

@override
VineAny requiredIfAnyMissing(List<String> 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;
}

Expand All @@ -60,7 +58,7 @@ final class VineAnySchema extends RuleParser implements VineAny {

@override
VineAny clone() {
return VineAnySchema(Queue.of(rules));
return VineAnySchema([...rules]);
}

@override
Expand Down
Loading