Skip to content

Commit b0b9bd9

Browse files
committed
tests: implement date schema and transforme method
1 parent c7c848a commit b0b9bd9

16 files changed

+568
-4
lines changed

lib/src/contracts/schema.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ abstract interface class BasicSchema<T extends VineSchema> {
3838

3939
T requiredIfAnyMissing(List<String> values);
4040

41+
T transform(Function(VineValidationContext ctx, FieldContext field) fn);
42+
4143
T nullable();
4244

4345
T optional();
@@ -136,3 +138,17 @@ abstract interface class VineArray implements VineSchema, BasicSchema<VineArray>
136138

137139

138140
abstract interface class VineUnion implements VineSchema, BasicSchema<VineUnion> {}
141+
142+
abstract interface class VineDate implements VineSchema, BasicSchema<VineDate> {
143+
VineDate before(DateTime value, {String? message});
144+
145+
VineDate after(DateTime value, {String? message});
146+
147+
VineDate between(DateTime min, DateTime max, {String? message});
148+
149+
VineDate beforeField(String value, {String? message});
150+
151+
VineDate afterField(String value, {String? message});
152+
153+
VineDate betweenFields(String start, String end, {String? message});
154+
}

lib/src/mapped_errors.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,12 @@ const mappedErrors = {
3939
'notInList': 'The field {name} must not be one of the specified values {values}',
4040
'requiredIfExists': 'The field {name} is required',
4141
'requiredIfExistsAny': 'The field {name} is required',
42+
'date': 'The field {name} must be a valid date',
43+
'date.required': 'The field {name} is required',
44+
'date.before': 'The field {name} must be before {date}',
45+
'date.after': 'The field {name} must be after {date}',
46+
'date.between': 'The field {name} must be between {start} and {end}',
47+
'date.beforeField': 'The field {name} must be before {field}',
48+
'date.afterField': 'The field {name} must be after {field}',
49+
'date.betweenFields': 'The field {name} must be between {start} and {end}',
4250
};

lib/src/rules/basic_rule.dart

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ void optionalRuleHandler(VineValidationContext ctx, FieldContext field) {
1313
}
1414
}
1515

16-
void requiredIfExistsRuleHandler(VineValidationContext ctx, FieldContext field, List<String> values) {
16+
void requiredIfExistsRuleHandler(
17+
VineValidationContext ctx, FieldContext field, List<String> values) {
1718
final currentContext = ctx.getFieldContext(field.customKeys);
1819
List<bool> matchs = [];
1920

@@ -29,7 +30,8 @@ void requiredIfExistsRuleHandler(VineValidationContext ctx, FieldContext field,
2930
}
3031
}
3132

32-
void requiredIfAnyExistsRuleHandler(VineValidationContext ctx, FieldContext field, List<String> values) {
33+
void requiredIfAnyExistsRuleHandler(
34+
VineValidationContext ctx, FieldContext field, List<String> values) {
3335
final currentContext = ctx.getFieldContext(field.customKeys);
3436
bool hasMatch = false;
3537

@@ -48,7 +50,8 @@ void requiredIfAnyExistsRuleHandler(VineValidationContext ctx, FieldContext fiel
4850
}
4951
}
5052

51-
void requiredIfMissingRuleHandler(VineValidationContext ctx, FieldContext field, List<String> values) {
53+
void requiredIfMissingRuleHandler(
54+
VineValidationContext ctx, FieldContext field, List<String> values) {
5255
final currentContext = ctx.getFieldContext(field.customKeys);
5356
List<bool> matchs = [];
5457

@@ -64,7 +67,8 @@ void requiredIfMissingRuleHandler(VineValidationContext ctx, FieldContext field,
6467
}
6568
}
6669

67-
void requiredIfAnyMissingRuleHandler(VineValidationContext ctx, FieldContext field, List<String> values) {
70+
void requiredIfAnyMissingRuleHandler(
71+
VineValidationContext ctx, FieldContext field, List<String> values) {
6872
final currentContext = ctx.getFieldContext(field.customKeys);
6973
bool hasMatch = false;
7074

@@ -82,3 +86,9 @@ void requiredIfAnyMissingRuleHandler(VineValidationContext ctx, FieldContext fie
8286
field.canBeContinue = false;
8387
}
8488
}
89+
90+
void transformRuleHandler(VineValidationContext ctx, FieldContext field,
91+
Function(VineValidationContext, FieldContext) fn) {
92+
final result = fn(ctx, field);
93+
field.mutate(result);
94+
}

lib/src/rules/date_rule.dart

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import 'package:vine/src/contracts/vine.dart';
2+
3+
void dateRuleHandler(VineValidationContext ctx, FieldContext field, String? message) {
4+
if (field.value is MissingValue) {
5+
final error = ctx.errorReporter.format('date.required', field, message, {});
6+
ctx.errorReporter.report('date.required', [...field.customKeys, field.name], error);
7+
return;
8+
}
9+
10+
final date = field.value is DateTime ? field.value : DateTime.tryParse(field.value);
11+
if (date != null) {
12+
field.mutate(date);
13+
return;
14+
}
15+
16+
final error = ctx.errorReporter.format('date', field, message, {});
17+
ctx.errorReporter.report('date', [...field.customKeys, field.name], error);
18+
}
19+
20+
void beforeDateRuleHandler(
21+
VineValidationContext ctx, FieldContext field, DateTime date, String? message) {
22+
if (field.value case DateTime value when !value.isBefore(date)) {
23+
final error = ctx.errorReporter.format('date.before', field, message, {
24+
'date': date,
25+
});
26+
27+
ctx.errorReporter.report('date.before', [...field.customKeys, field.name], error);
28+
}
29+
}
30+
31+
void afterDateRuleHandler(
32+
VineValidationContext ctx, FieldContext field, DateTime date, String? message) {
33+
if (field.value case DateTime value when !value.isAfter(date)) {
34+
final error = ctx.errorReporter.format('date.after', field, message, {
35+
'date': date,
36+
});
37+
38+
ctx.errorReporter.report('date.after', [...field.customKeys, field.name], error);
39+
}
40+
}
41+
42+
void betweenDateRuleHandler(
43+
VineValidationContext ctx, FieldContext field, DateTime start, DateTime end, String? message) {
44+
if (field.value case DateTime value when !(value.isAfter(start) && value.isBefore(end))) {
45+
final error = ctx.errorReporter.format('date.between', field, message, {
46+
'start': start,
47+
'end': end,
48+
});
49+
50+
ctx.errorReporter.report('date.between', [...field.customKeys, field.name], error);
51+
}
52+
}
53+
54+
void beforeFieldDateRuleHandler(
55+
VineValidationContext ctx, FieldContext field, String targetField, String? message) {
56+
final currentContext = ctx.getFieldContext(field.customKeys);
57+
final DateTime? targetFieldDate = currentContext[targetField] != null
58+
? currentContext[targetField] is DateTime
59+
? currentContext[targetField]
60+
: DateTime.tryParse(currentContext[targetField])
61+
: null;
62+
63+
if (field.value case DateTime value
64+
when targetFieldDate != null && !value.isBefore(targetFieldDate)) {
65+
final error = ctx.errorReporter.format('date.beforeField', field, message, {
66+
'field': targetField,
67+
});
68+
69+
ctx.errorReporter.report('date.beforeField', [...field.customKeys, field.name], error);
70+
}
71+
}
72+
73+
void afterFieldDateRuleHandler(
74+
VineValidationContext ctx, FieldContext field, String targetField, String? message) {
75+
final currentContext = ctx.getFieldContext(field.customKeys);
76+
final DateTime? targetFieldDate = currentContext[targetField] != null
77+
? currentContext[targetField] is DateTime
78+
? currentContext[targetField]
79+
: DateTime.tryParse(currentContext[targetField])
80+
: null;
81+
82+
if (field.value case DateTime value
83+
when targetFieldDate != null && !value.isAfter(targetFieldDate)) {
84+
final error = ctx.errorReporter.format('date.targetField', field, message, {
85+
'field': targetField,
86+
});
87+
88+
ctx.errorReporter.report('date.targetField', [...field.customKeys, field.name], error);
89+
}
90+
}
91+
92+
void betweenFieldDateRuleHandler(VineValidationContext ctx, FieldContext field, String startField,
93+
String endField, String? message) {
94+
final currentContext = ctx.getFieldContext(field.customKeys);
95+
final DateTime? startFieldDate = currentContext[startField] != null
96+
? currentContext[startField] is DateTime
97+
? currentContext[startField]
98+
: DateTime.tryParse(currentContext[startField])
99+
: null;
100+
101+
final DateTime? endFieldDate = currentContext[endField] != null
102+
? currentContext[endField] is DateTime
103+
? currentContext[endField]
104+
: DateTime.tryParse(currentContext[endField])
105+
: null;
106+
107+
if (startFieldDate == null) {
108+
final error = ctx.errorReporter.format('date.required', field, message, {'field': startField});
109+
ctx.errorReporter.report('date.required', [...field.customKeys, field.name], error);
110+
return;
111+
}
112+
113+
if (endFieldDate == null) {
114+
final error = ctx.errorReporter.format('date.required', field, message, {'field': endField});
115+
ctx.errorReporter.report('date.required', [...field.customKeys, field.name], error);
116+
return;
117+
}
118+
119+
if (field.value case DateTime value
120+
when !(value.isAfter(startFieldDate) && value.isBefore(endFieldDate))) {
121+
final error = ctx.errorReporter.format('date.between', field, message, {
122+
'start': startField,
123+
'end': endField,
124+
});
125+
126+
ctx.errorReporter.report('date.betweenFields', [...field.customKeys, field.name], error);
127+
}
128+
}

lib/src/schema/any_schema.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:collection';
22

33
import 'package:vine/src/contracts/schema.dart';
4+
import 'package:vine/src/contracts/vine.dart';
45
import 'package:vine/src/rule_parser.dart';
56
import 'package:vine/src/rules/basic_rule.dart';
67

@@ -31,6 +32,12 @@ final class VineAnySchema extends RuleParser implements VineAny {
3132
return this;
3233
}
3334

35+
@override
36+
VineAny transform(Function(VineValidationContext, FieldContext) fn) {
37+
super.addRule((ctx, field) => transformRuleHandler(ctx, field, fn));
38+
return this;
39+
}
40+
3441
@override
3542
VineAny nullable() {
3643
super.isNullable = true;

lib/src/schema/array_schema.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:collection';
22

33
import 'package:vine/src/contracts/schema.dart';
4+
import 'package:vine/src/contracts/vine.dart';
45
import 'package:vine/src/rule_parser.dart';
56
import 'package:vine/src/rules/array_rule.dart';
67
import 'package:vine/src/rules/basic_rule.dart';
@@ -50,6 +51,12 @@ final class VineArraySchema extends RuleParser implements VineArray {
5051
return this;
5152
}
5253

54+
@override
55+
VineArray transform(Function(VineValidationContext, FieldContext) fn) {
56+
super.addRule((ctx, field) => transformRuleHandler(ctx, field, fn));
57+
return this;
58+
}
59+
5360
@override
5461
VineArray nullable() {
5562
super.isNullable = true;

lib/src/schema/boolean_schema.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:collection';
22

33
import 'package:vine/src/contracts/schema.dart';
4+
import 'package:vine/src/contracts/vine.dart';
45
import 'package:vine/src/rule_parser.dart';
56
import 'package:vine/src/rules/basic_rule.dart';
67

@@ -31,6 +32,12 @@ final class VineBooleanSchema extends RuleParser implements VineBoolean {
3132
return this;
3233
}
3334

35+
@override
36+
VineBoolean transform(Function(VineValidationContext, FieldContext) fn) {
37+
super.addRule((ctx, field) => transformRuleHandler(ctx, field, fn));
38+
return this;
39+
}
40+
3441
@override
3542
VineBoolean nullable() {
3643
super.isNullable = true;

lib/src/schema/date_schema.dart

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import 'dart:collection';
2+
3+
import 'package:vine/src/contracts/schema.dart';
4+
import 'package:vine/src/contracts/vine.dart';
5+
import 'package:vine/src/rule_parser.dart';
6+
import 'package:vine/src/rules/basic_rule.dart';
7+
import 'package:vine/src/rules/date_rule.dart';
8+
9+
final class VineDateSchema extends RuleParser implements VineDate {
10+
VineDateSchema(super._rules);
11+
12+
@override
13+
VineDate before(DateTime value, {String? message}) {
14+
super.addRule((ctx, field) => beforeDateRuleHandler(ctx, field, value, message));
15+
return this;
16+
}
17+
18+
@override
19+
VineDate after(DateTime value, {String? message}) {
20+
super.addRule((ctx, field) => afterDateRuleHandler(ctx, field, value, message));
21+
return this;
22+
}
23+
24+
@override
25+
VineDate between(DateTime min, DateTime max, {String? message}) {
26+
super.addRule((ctx, field) => betweenDateRuleHandler(ctx, field, min, max, message));
27+
return this;
28+
}
29+
30+
@override
31+
VineDate beforeField(String target, {String? message}) {
32+
super.addRule((ctx, field) => beforeFieldDateRuleHandler(ctx, field, target, message));
33+
return this;
34+
}
35+
36+
@override
37+
VineDate afterField(String target, {String? message}) {
38+
super.addRule((ctx, field) => beforeFieldDateRuleHandler(ctx, field, target, message));
39+
return this;
40+
}
41+
42+
@override
43+
VineDate betweenFields(String start, String end, {String? message}) {
44+
super.addRule((ctx, field) => betweenFieldDateRuleHandler(ctx, field, start, end, message));
45+
return this;
46+
}
47+
48+
@override
49+
VineDate transform(Function(VineValidationContext, FieldContext) fn) {
50+
super.addRule((ctx, field) => transformRuleHandler(ctx, field, fn));
51+
return this;
52+
}
53+
54+
@override
55+
VineDate requiredIfExist(List<String> values) {
56+
super.addRule((ctx, field) => requiredIfExistsRuleHandler(ctx, field, values), positioned: true);
57+
return this;
58+
}
59+
60+
@override
61+
VineDate requiredIfAnyExist(List<String> values) {
62+
super.addRule((ctx, field) => requiredIfAnyExistsRuleHandler(ctx, field, values), positioned: true);
63+
return this;
64+
}
65+
66+
@override
67+
VineDate requiredIfMissing(List<String> values) {
68+
super.addRule((ctx, field) => requiredIfMissingRuleHandler(ctx, field, values), positioned: true);
69+
return this;
70+
}
71+
72+
@override
73+
VineDate requiredIfAnyMissing(List<String> values) {
74+
super.addRule((ctx, field) => requiredIfAnyMissingRuleHandler(ctx, field, values), positioned: true);
75+
return this;
76+
}
77+
78+
@override
79+
VineDate nullable() {
80+
super.isNullable = true;
81+
return this;
82+
}
83+
84+
@override
85+
VineDate optional() {
86+
super.isOptional = true;
87+
return this;
88+
}
89+
90+
@override
91+
VineDate clone() {
92+
return VineDateSchema(Queue.of(rules));
93+
}
94+
}

lib/src/schema/enum_schema.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:collection';
33
import 'package:vine/src/contracts/schema.dart';
44
import 'package:vine/src/rule_parser.dart';
55
import 'package:vine/src/rules/basic_rule.dart';
6+
import 'package:vine/vine.dart';
67

78
final class VineEnumSchema extends RuleParser implements VineEnum {
89
VineEnumSchema(super._rules);
@@ -31,6 +32,12 @@ final class VineEnumSchema extends RuleParser implements VineEnum {
3132
return this;
3233
}
3334

35+
@override
36+
VineEnum transform(Function(VineValidationContext, FieldContext) fn) {
37+
super.addRule((ctx, field) => transformRuleHandler(ctx, field, fn));
38+
return this;
39+
}
40+
3441
@override
3542
VineEnum nullable() {
3643
super.isNullable = true;

0 commit comments

Comments
 (0)