Skip to content

Commit

Permalink
Merge pull request #3 from Flutterando/feature/is-not-null
Browse files Browse the repository at this point in the history
[DRAFT] feature: initial proposal of 'is not null'
  • Loading branch information
jacobaraujo7 authored Dec 19, 2024
2 parents 619be13 + 176700f commit 5716ae2
Show file tree
Hide file tree
Showing 59 changed files with 2,661 additions and 83 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.2.0

* Added support nullable

## 1.1.0

* Added Label
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ Here’s a complete list of available validators you can use:
- **inclusiveBetween**: Checks if the datetime value is between two datetime values, including both bounds.
- **exclusiveBetween**: Checks if the datetime value is between two datetime values, excluding both bounds.

**Observation: for almost all validators, there is an equivalent with the `OrNull` suffix. Example: `validEmailOrNull`**

## Usage with Flutter

If you’re using the `lucid_validation` package in a Flutter app, integrating with `TextFormField` is straightforward.
Expand Down Expand Up @@ -200,7 +202,7 @@ Example:
```dart
ruleFor((user) => user.phoneNumber, key: 'phoneNumber')
.when((user) => user.requiresPhoneNumber)
.must((value) => value.isNotEmpty, 'Phone number is required', 'phone_required')
.isEmpty()
.must((value) => value.length == 10, 'Phone number must be 10 digits', 'phone_length');
```

Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ packages:
path: ".."
relative: true
source: path
version: "1.0.1"
version: "1.1.0"
matcher:
dependency: transitive
description:
Expand Down
28 changes: 27 additions & 1 deletion lib/src/lucid_validation_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,14 @@ abstract class LucidValidationBuilder<TProp, Entity> {
final List<RuleFunc<Entity>> _rules = [];
var _mode = CascadeMode.continueExecution;
LucidValidator<TProp>? _nestedValidator;
LucidValidator _lucid;

bool Function(Entity entity)? _condition;

/// Creates a [LucidValidationBuilder] instance with an optional [key].
///
/// The [key] can be used to identify this specific validation in a larger validation context.
LucidValidationBuilder(this.key, this.label, this._selector);
LucidValidationBuilder(this.key, this.label, this._selector, this._lucid);

String? Function([String?]) nestedByField(Entity entity, String key) {
if (_nestedValidator == null) {
Expand Down Expand Up @@ -154,6 +155,26 @@ abstract class LucidValidationBuilder<TProp, Entity> {
return this;
}

LucidValidationBuilder<T, Entity> useNotNull<T extends Object>(
ValidationException? Function(TProp value, Entity entity) rule) {
_rules.add((entity) => rule(_selector(entity), entity));

final builder =
_LucidValidationBuilder<T, Entity>(key, label, (Entity entity) {
final value = _selector(entity) as T;
return value;
}, _lucid);

this._mode = CascadeMode.stopOnFirstFailure;

_lucid.addBuilder(builder);
return builder;
}

CascadeMode getMode() {
return this._mode;
}

/// Sets the [CascadeMode] for the validation rules associated with this property.
///
/// The [cascade] method allows you to control the behavior of rule execution when a validation failure occurs.
Expand Down Expand Up @@ -261,3 +282,8 @@ abstract class LucidValidationBuilder<TProp, Entity> {
return exceptions;
}
}

class _LucidValidationBuilder<TProp, Entity>
extends LucidValidationBuilder<TProp, Entity> {
_LucidValidationBuilder(super.key, super.label, super.selector, super.lucid);
}
20 changes: 14 additions & 6 deletions lib/src/lucid_validator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import '../lucid_validation.dart';
abstract class LucidValidator<E> {
final List<LucidValidationBuilder<dynamic, E>> _builders = [];

addBuilder(LucidValidationBuilder<dynamic, E> builder) {
this._builders.add(builder);
}

/// Registers a validation rule for a specific property of the entity.
///
/// [func] is a function that selects the property from the entity [E].
Expand All @@ -22,7 +26,7 @@ abstract class LucidValidator<E> {
TProp Function(E entity) selector,
{required String key,
String label = ''}) {
final builder = _LucidValidationBuilder<TProp, E>(key, label, selector);
final builder = _LucidValidationBuilder<TProp, E>(key, label, selector, this);
_builders.add(builder);

return builder;
Expand Down Expand Up @@ -89,10 +93,14 @@ abstract class LucidValidator<E> {
/// }
/// ```
ValidationResult validate(E entity) {
final exceptions =
_builders.fold(<ValidationException>[], (previousErrors, builder) {
return previousErrors..addAll(builder.executeRules(entity));
});
final List<ValidationException> exceptions = [];

for (var builder in _builders) {
exceptions.addAll(builder.executeRules(entity));
if(builder.getMode() == CascadeMode.stopOnFirstFailure && exceptions.isNotEmpty) {
break;
}
}

return ValidationResult(
isValid: exceptions.isEmpty,
Expand All @@ -103,5 +111,5 @@ abstract class LucidValidator<E> {

class _LucidValidationBuilder<TProp, Entity>
extends LucidValidationBuilder<TProp, Entity> {
_LucidValidationBuilder(super.key, super.label, super.selector);
_LucidValidationBuilder(super.key, super.label, super.selector, super.lucid);
}
96 changes: 96 additions & 0 deletions lib/src/validations/exclusive_between_datetime_validation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,99 @@ extension ExclusiveBetweenDatetimeValidation
});
}
}

extension ExclusiveBetweenDatetimeNullableValidation
on SimpleValidationBuilder<DateTime?> {
/// Adds a validation rule that checks if the [DateTime?] is greater than [comparison].
///
/// [start] is the date and time value must be greater than.
/// [end] is the date and time value must be less than.
/// [message] is the error message returned if the validation fails.
/// [code] is an optional error code for translation purposes.
///
/// Returns the [LucidValidationBuilder] to allow for method chaining.
///
/// Example:
/// ```dart
/// ...
/// .ruleFor((event) => event.start, key: 'start') // event.start is nullable
/// .exclusiveBetween(start: DateTime.now(), end: DateTime.now().add(Duration(days: 1)));
/// ```
///
/// String format args:
/// - **{PropertyName}**: The name of the property.
/// - **{StartValue}**: The value must be greater than or equal to.
/// - **{EndValue}**: The value must be less than or equal to.
///
SimpleValidationBuilder<DateTime?> exclusiveBetween({
required DateTime start,
required DateTime end,
String? message,
String? code,
}) {
return use((value, entity) {
if (value != null && (value.isAfter(start) && value.isBefore(end))) return null;

final currentCode = code ?? Language.code.exclusiveBetweenDatetime;
final currentMessage = LucidValidation.global.languageManager.translate(
currentCode,
parameters: {
'PropertyName': label.isNotEmpty ? label : key,
'StartValue': start.toString(),
'EndValue': end.toString(),
},
defaultMessage: message,
);

return ValidationException(message: currentMessage, code: currentCode);
});
}
}

extension ExclusiveBetweenDatetimeOrNullableValidation
on SimpleValidationBuilder<DateTime?> {
/// Adds a validation rule that checks if the [DateTime?] is greater than [comparison] or [null].
///
/// [start] is the date and time value must be greater than.
/// [end] is the date and time value must be less than.
/// [message] is the error message returned if the validation fails.
/// [code] is an optional error code for translation purposes.
///
/// Returns the [LucidValidationBuilder] to allow for method chaining.
///
/// Example:
/// ```dart
/// ...
/// .ruleFor((event) => event.start, key: 'start') //
/// .exclusiveBetweenOrNull(start: DateTime.now(), end: DateTime.now().add(Duration(days: 1)));
/// ```
///
/// String format args:
/// - **{PropertyName}**: The name of the property.
/// - **{StartValue}**: The value must be greater than or equal to.
/// - **{EndValue}**: The value must be less than or equal to.
///
SimpleValidationBuilder<DateTime?> exclusiveBetweenOrNull({
required DateTime start,
required DateTime end,
String? message,
String? code,
}) {
return use((value, entity) {
if (value == null || (value.isAfter(start) && value.isBefore(end))) return null;

final currentCode = code ?? Language.code.exclusiveBetweenDatetime;
final currentMessage = LucidValidation.global.languageManager.translate(
currentCode,
parameters: {
'PropertyName': label.isNotEmpty ? label : key,
'StartValue': start.toString(),
'EndValue': end.toString(),
},
defaultMessage: message,
);

return ValidationException(message: currentMessage, code: currentCode);
});
}
}
86 changes: 86 additions & 0 deletions lib/src/validations/greater_than_datetime_validation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,89 @@ extension GreaterThanDateTimeValidation on SimpleValidationBuilder<DateTime> {
});
}
}

extension GreaterThanDateTimeNullableValidation on SimpleValidationBuilder<DateTime?> {
/// Adds a validation rule that checks if the [DateTime?] is greater than [comparison].
///
/// [comparison] is the date and time value must be greater than.
/// [message] is the error message returned if the validation fails.
/// [code] is an optional error code for translation purposes.
///
/// Returns the [LucidValidationBuilder] to allow for method chaining.
///
/// Example:
/// ```dart
/// ...
/// .ruleFor((event) => event.start, key: 'start') // event.start is nullable
/// .greaterThan(DateTime.now());
/// ```
///
/// String format args:
/// - **{PropertyName}**: The name of the property.
/// - **{ComparisonValue}**: The value to compare against.
///
SimpleValidationBuilder<DateTime?> greaterThan(
DateTime comparison, {
String? message,
String? code,
}) {
return use((value, entity) {
if (value != null && value.isAfter(comparison)) return null;

final currentCode = code ?? Language.code.greaterThanDatetime;
final currentMessage = LucidValidation.global.languageManager.translate(
currentCode,
parameters: {
'PropertyName': label.isNotEmpty ? label : key,
'ComparisonValue': comparison.toString(),
},
defaultMessage: message,
);

return ValidationException(message: currentMessage, code: currentCode);
});
}
}

extension GreaterThanDateTimeOrNullableValidation on SimpleValidationBuilder<DateTime?> {
/// Adds a validation rule that checks if the [DateTime?] is greater than [comparison] or [null].
///
/// [comparison] is the date and time value must be greater than.
/// [message] is the error message returned if the validation fails.
/// [code] is an optional error code for translation purposes.
///
/// Returns the [LucidValidationBuilder] to allow for method chaining.
///
/// Example:
/// ```dart
/// ...
/// .ruleFor((event) => event.start, key: 'start') //
/// .greaterThanOrNull(DateTime.now());
/// ```
///
/// String format args:
/// - **{PropertyName}**: The name of the property.
/// - **{ComparisonValue}**: The value to compare against.
///
SimpleValidationBuilder<DateTime?> greaterThanOrNull(
DateTime comparison, {
String? message,
String? code,
}) {
return use((value, entity) {
if (value == null || value.isAfter(comparison)) return null;

final currentCode = code ?? Language.code.greaterThanDatetime;
final currentMessage = LucidValidation.global.languageManager.translate(
currentCode,
parameters: {
'PropertyName': label.isNotEmpty ? label : key,
'ComparisonValue': comparison.toString(),
},
defaultMessage: message,
);

return ValidationException(message: currentMessage, code: currentCode);
});
}
}
Loading

0 comments on commit 5716ae2

Please sign in to comment.