1
1
namespace Danom . Validation ;
2
2
3
+ using System . Text . RegularExpressions ;
3
4
using FluentValidation ;
4
5
using FluentValidation . Validators ;
5
6
7
+ internal static partial class ValidationHelpers
8
+ {
9
+ internal static bool Validate < T , TValue > (
10
+ ValidationContext < T > context ,
11
+ IValidator < TValue > validator ,
12
+ TValue instance )
13
+ {
14
+ var validationResult = validator . Validate ( instance ) ;
15
+
16
+ if ( ! validationResult . IsValid )
17
+ {
18
+ var quotedDisplayName = string . Concat ( "'" , context . DisplayName , "'" ) ;
19
+ validationResult . Errors . ForEach ( e =>
20
+ context . AddFailure (
21
+ propertyName : context . DisplayName ,
22
+ errorMessage : ValidationHelpers . ReplaceMissingDisplayName ( e . ErrorMessage , quotedDisplayName ) ) ) ;
23
+ }
24
+
25
+ return validationResult . IsValid ;
26
+ }
27
+
28
+ static string ReplaceMissingDisplayName ( string errorMessage , string properDisplayName ) =>
29
+ QuotedDisplayNameRegex ( ) . Replace ( errorMessage , properDisplayName ) ;
30
+
31
+ [ GeneratedRegex ( @"''" ) ]
32
+ private static partial Regex QuotedDisplayNameRegex ( ) ;
33
+ }
34
+
6
35
/// <summary>
7
- /// A validator for <see cref="Option{TValue}"/> that validates the value if it is Some.
36
+ /// A validator for <see cref="Option{TValue}"/> that validates the value if it
37
+ /// is Some, otherwise the value is considered valid.
8
38
/// </summary>
9
39
/// <typeparam name="T"></typeparam>
10
40
/// <typeparam name="TValue"></typeparam>
11
41
/// <param name="validator"></param>
12
- public class OptionValidator < T , TValue > ( IValidator < TValue > validator ) : PropertyValidator < T , Option < TValue > >
42
+ public sealed class OptionalValidator < T , TValue > ( IValidator < TValue > validator )
43
+ : PropertyValidator < T , Option < TValue > >
13
44
{
14
45
/// <summary>
15
46
/// Specifies the name of the validator.
@@ -24,15 +55,71 @@ public class OptionValidator<T, TValue>(IValidator<TValue> validator) : Property
24
55
/// <returns></returns>
25
56
public override bool IsValid ( ValidationContext < T > context , Option < TValue > value ) =>
26
57
value . Match (
27
- some : x => validator . Validate ( x ) . IsValid ,
58
+ some : x => ValidationHelpers . Validate ( context , validator , x ) ,
28
59
none : ( ) => true ) ;
60
+
61
+ /// <summary>
62
+ /// Returns the default error message template for the validator.
63
+ /// </summary>
64
+ /// <param name="errorCode"></param>
65
+ /// <returns></returns>
66
+ protected override string GetDefaultMessageTemplate ( string errorCode ) =>
67
+ "'{PropertyName}' is optional, but invalid." ;
68
+ }
69
+
70
+ /// <summary>
71
+ /// A validator for <see cref="Option{TValue}"/> that validates the value if it
72
+ /// is Some, otherwise the value is considered invalid.
73
+ /// </summary>
74
+ /// <typeparam name="T"></typeparam>
75
+ /// <typeparam name="TValue"></typeparam>
76
+ /// <param name="validator"></param>
77
+ public class RequiredValidator < T , TValue > ( IValidator < TValue > validator ) : PropertyValidator < T , Option < TValue > >
78
+ {
79
+ /// <summary>
80
+ /// Specifies the name of the validator.
81
+ /// </summary>
82
+ public override string Name => "OptionNotNoneValidator" ;
83
+
84
+ /// <summary>
85
+ /// Returns whether the Option is valid given the specified context.
86
+ /// </summary>
87
+ /// <param name="context"></param>
88
+ /// <param name="value"></param>
89
+ /// <returns></returns>
90
+ public override bool IsValid ( ValidationContext < T > context , Option < TValue > value ) =>
91
+ value . Match (
92
+ some : x => ValidationHelpers . Validate ( context , validator , x ) ,
93
+ none : ( ) => false ) ;
94
+
95
+ /// <summary>
96
+ /// Returns the default error message template for the validator.
97
+ /// </summary>
98
+ /// <param name="errorCode"></param>
99
+ /// <returns></returns>
100
+ protected override string GetDefaultMessageTemplate ( string errorCode ) =>
101
+ "'{PropertyName}' is required and invalid or missing." ;
29
102
}
30
103
31
104
/// <summary>
32
105
/// Contains extension methods for <see cref="IRuleBuilder{T, TValue}"/>.
33
106
/// </summary>
34
107
public static class OptionValidatorExtensions
35
108
{
109
+ /// <summary>
110
+ /// Set the validator for an Option value. The value is considered valid if
111
+ /// it is None.
112
+ /// </summary>
113
+ /// <typeparam name="T"></typeparam>
114
+ /// <typeparam name="TValue"></typeparam>
115
+ /// <param name="ruleBuilder"></param>
116
+ /// <param name="validator"></param>
117
+ /// <returns></returns>
118
+ public static IRuleBuilder < T , Option < TValue > > Optional < T , TValue > (
119
+ this IRuleBuilder < T , Option < TValue > > ruleBuilder ,
120
+ IValidator < TValue > validator ) =>
121
+ ruleBuilder . SetValidator ( new OptionalValidator < T , TValue > ( validator ) ) ;
122
+
36
123
/// <summary>
37
124
/// Specifies a validator for the value if it is Some, otherwise the value
38
125
/// is considered valid.
@@ -42,28 +129,56 @@ public static class OptionValidatorExtensions
42
129
/// <param name="ruleBuilder"></param>
43
130
/// <param name="action"></param>
44
131
/// <returns></returns>
45
- public static IRuleBuilder < T , Option < TValue > > WhenSome < T , TValue > (
132
+ public static IRuleBuilder < T , Option < TValue > > Optional < T , TValue > (
46
133
this IRuleBuilder < T , Option < TValue > > ruleBuilder ,
47
134
Action < IRuleBuilder < TValue , TValue > > action )
48
135
{
49
136
var inlineValidator = new InlineValidator < TValue > ( ) ;
50
137
action ( inlineValidator . RuleFor ( x => x ) ) ;
51
- var optionValidator = new OptionValidator < T , TValue > ( inlineValidator ) ;
138
+ var optionValidator = new OptionalValidator < T , TValue > ( inlineValidator ) ;
52
139
return ruleBuilder . SetValidator ( optionValidator ) ;
53
140
}
54
141
55
142
/// <summary>
56
- /// Set the validator for an Option value.
143
+ /// Indicates that an <see cref="Option{T}"/> is required to have a value.
144
+ /// </summary>
145
+ /// <typeparam name="T"></typeparam>
146
+ /// <typeparam name="TValue"></typeparam>
147
+ /// <param name="ruleBuilder"></param>
148
+ /// <returns></returns>
149
+ public static IRuleBuilder < T , Option < TValue > > Required < T , TValue > (
150
+ this IRuleBuilder < T , Option < TValue > > ruleBuilder ) =>
151
+ ruleBuilder . SetValidator ( new RequiredValidator < T , TValue > ( new InlineValidator < TValue > ( ) ) ) ;
152
+
153
+ /// <summary>
154
+ /// Indicates that an <see cref="Option{T}"/> is required to have a value.
57
155
/// </summary>
58
156
/// <typeparam name="T"></typeparam>
59
157
/// <typeparam name="TValue"></typeparam>
60
158
/// <param name="ruleBuilder"></param>
61
159
/// <param name="validator"></param>
62
160
/// <returns></returns>
63
- public static IRuleBuilder < T , Option < TValue > > SetValidator < T , TValue > (
161
+ public static IRuleBuilder < T , Option < TValue > > Required < T , TValue > (
162
+ this IRuleBuilder < T , Option < TValue > > ruleBuilder ,
163
+ IValidator < TValue > validator ) =>
164
+ ruleBuilder . SetValidator ( new RequiredValidator < T , TValue > ( validator ) ) ;
165
+
166
+ /// <summary>
167
+ /// Indicates that an <see cref="Option{T}"/> is required to have a value.
168
+ /// </summary>
169
+ /// <typeparam name="T"></typeparam>
170
+ /// <typeparam name="TValue"></typeparam>
171
+ /// <param name="ruleBuilder"></param>
172
+ /// <param name="action"></param>
173
+ /// <returns></returns>
174
+ public static IRuleBuilder < T , Option < TValue > > Required < T , TValue > (
64
175
this IRuleBuilder < T , Option < TValue > > ruleBuilder ,
65
- IValidator < TValue > validator )
176
+ Action < IRuleBuilder < TValue , TValue > > action )
66
177
{
67
- return ruleBuilder . SetValidator ( new OptionValidator < T , TValue > ( validator ) ) ;
178
+ var inlineValidator = new InlineValidator < TValue > ( ) ;
179
+ action ( inlineValidator . RuleFor ( x => x ) ) ;
180
+ var optionValidator = new RequiredValidator < T , TValue > ( inlineValidator ) ;
181
+ return ruleBuilder . SetValidator ( optionValidator ) ;
68
182
}
183
+
69
184
}
0 commit comments