Skip to content

Commit

Permalink
feature: add validCPFOrCNPJ
Browse files Browse the repository at this point in the history
  • Loading branch information
wellgenio committed Jan 23, 2025
1 parent f2573b8 commit 57778e7
Show file tree
Hide file tree
Showing 10 changed files with 345 additions and 15 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.2

* Added validCPFOrCNPJ

## 1.2.1

* Added getExceptions and getExceptionsByKey
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ Here’s a complete list of available validators you can use:
- **validCPF**: Checks if a string is a valid CPF (for use in Brazil).
- **validCNPJ**: Checks if a string is a valid CNPJ (for use in Brazil).
- **validCEP**: Checks if a string is a valid CEP (for use in Brazil).
- **validCPFOrCNPJ**: Checks if a string is a valid CPF or CNPJ (for use in Brazil).
- **validCredCard**: Checks if a string is a valid Credit Card.
- **greaterThanOrEqualTo**: Checks if the datetime value is greater than or equal to a specified minimum datetime.
- **greaterThan**: Checks if the datetime value is greater than a specified minimum datetime.
Expand Down
4 changes: 3 additions & 1 deletion lib/src/localization/language.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ abstract class Language {
lessThanDateTime: 'lessThanDateTime',
inclusiveBetweenDatetime: 'inclusiveBetweenDatetime',
exclusiveBetweenDatetime: 'exclusiveBetweenDatetime',
validCpfOrCnpj: 'validCpfOrCnpj',
);

Language([Map<String, String> translations = const {}]) {
Expand Down Expand Up @@ -81,7 +82,8 @@ abstract class Language {
code.inclusiveBetweenDatetime:
"'{PropertyName}' must be greater than or equal to '{StartValue}' date and less than or equal to '{EndValue}' date.",
code.exclusiveBetweenDatetime:
"'{PropertyName}' must be greater than the '{StartValue}' date and less than the '{EndValue}' date."
"'{PropertyName}' must be greater than the '{StartValue}' date and less than the '{EndValue}' date.",
code.validCpfOrCnpj: "'{PropertyName}' is not a valid CPF or CNPJ."
};

String? getTranslation(String key) => _translations[key];
Expand Down
61 changes: 48 additions & 13 deletions lib/src/lucid_validator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ abstract class LucidValidator<E> {
TProp Function(E entity) selector,
{required String key,
String label = ''}) {
final builder = _LucidValidationBuilder<TProp, E>(key, label, selector, this);
final builder =
_LucidValidationBuilder<TProp, E>(key, label, selector, this);
_builders.add(builder);

return builder;
Expand All @@ -38,11 +39,14 @@ abstract class LucidValidator<E> {
///
/// Example:
/// ```dart
///
/// void callback(errors) {}
///
/// final validator = UserValidation();
/// final emailValidator = validator.byField('email');
/// final emailValidator = validator.byField(user, 'email', callback);
/// String? validationResult = emailValidator('[email protected]');
/// ```
String? Function([String?]) byField(E entity, String key) {
String? Function([String?]) byField(E entity, String key, [Function(List<ValidationException>)? callback]) {
if (key.contains('.')) {
final keys = key.split('.');

Expand All @@ -63,6 +67,7 @@ abstract class LucidValidator<E> {
return ([_]) {
final errors = builder.executeRules(entity);
if (errors.isNotEmpty) {
callback?.call(errors);
return errors.first.message;
}
return null;
Expand Down Expand Up @@ -97,7 +102,8 @@ abstract class LucidValidator<E> {

for (var builder in _builders) {
exceptions.addAll(builder.executeRules(entity));
if(builder.getMode() == CascadeMode.stopOnFirstFailure && exceptions.isNotEmpty) {
if (builder.getMode() == CascadeMode.stopOnFirstFailure &&
exceptions.isNotEmpty) {
break;
}
}
Expand All @@ -108,30 +114,59 @@ abstract class LucidValidator<E> {
);
}

/// **getExceptions**
///
/// This function fetches and returns all validation exceptions associated with an entity.
///
/// **Parameters:**
/// * `entity`: The entity to be validated.
///
/// **Return:**
/// * A list of validation exceptions encountered.
///
/// **Description:**
/// The function iterates over all registered validation builders and executes the validation rules for each builder.
/// Exceptions encountered are added to a list and returned at the end.
/// If the cascade mode is set to 'stopOnFirstFailure', the function stops checking the remaining rules after the first exception.
///
List<ValidationException> getExceptions(E entity) {
final List<ValidationException> exceptions = [];

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

return exceptions;
}

/// **getExceptions**
///
/// This function fetches and returns all validation exceptions associated with an entity by key.
///
/// **Parameters:**
/// * `entity`: The entity to be validated.
/// * `key`: key associated with validations.
///
/// **Return:**
/// * A list of validation exceptions encountered by key.
///
/// **Description:**
/// The function iterates over all registered validation builders and executes the validation rules for each builder.
/// Exceptions encountered are added to a list and returned at the end.
/// If the cascade mode is set to 'stopOnFirstFailure', the function stops checking the remaining rules after the first exception.
///
List<ValidationException> getExceptionsByKey(E entity, String key) {
final List<ValidationException> exceptions = [];
final builder = _getBuilderByKey(key);

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

final exceptions = builder.executeRules(entity);

return exceptions.where((exception) => exception.key == key).toList();
return exceptions.isNotEmpty ? exceptions : [];
}
}

Expand Down
231 changes: 231 additions & 0 deletions lib/src/validations/valid_cpf_or_cnpj_validation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
part of 'validations.dart';

extension ValidCpfOrCnpjValidation on SimpleValidationBuilder<String> {
/// Adds a validation rule that checks if the [String] is a valid CPF or CNPJ number.
///
/// The CPF is the national identifier for Brazilian individuals and the CNPJ is the national identifier for Brazilian companies.
/// This method verifies the format and the validity of the CPF or CNPJ, ensuring it follows the correct algorithm for digit verification.
///
/// [message] is the error message returned if the validation fails. Defaults to "Invalid CPF or CNPJ".
/// [code] is an optional error code for translation purposes.
///
/// Returns the [LucidValidationBuilder] to allow for method chaining.
///
/// Example:
/// ...
/// ruleFor((user) => user.cpfOrCnpj, key: 'cpfOrCnpj')
/// .validCpfOrCnpj();
///
///
/// String format args:
/// - **{PropertyName}**: The name of the property.
///
SimpleValidationBuilder<String> validCPFOrCNPJ(
{String? message, String? code}) {
return use((value, entity) {
if (_validateCPF(value) || _validateCNPJ(value)) return null;

if (value.length == 11) {
final currentCode = code ?? Language.code.validCpfOrCnpj;
final currentMessage = LucidValidation.global.languageManager.translate(
currentCode,
parameters: {
'PropertyName': label.isNotEmpty ? label : key,
},
defaultMessage: message,
);

return ValidationException(
message: currentMessage,
code: currentCode,
key: key,
);
} else if (value.length == 14) {
final currentCode = code ?? Language.code.validCpfOrCnpj;
final currentMessage = LucidValidation.global.languageManager.translate(
currentCode,
parameters: {
'PropertyName': label.isNotEmpty ? label : key,
},
defaultMessage: message,
);

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

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

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

extension ValidCpfOrCnpjNullableValidation on SimpleValidationBuilder<String?> {
/// Adds a validation rule that checks if the [String] is a valid CPF or CNPJ number.
///
/// The CPF is the national identifier for Brazilian individuals and the CNPJ is the national identifier for Brazilian companies.
/// This method verifies the format and the validity of the CPF or CNPJ, ensuring it follows the correct algorithm for digit verification.
///
/// [message] is the error message returned if the validation fails. Defaults to "Invalid CPF or CNPJ".
/// [code] is an optional error code for translation purposes.
///
/// Returns the [LucidValidationBuilder] to allow for method chaining.
///
/// Example:
/// ...
/// ruleFor((user) => user.cpfOrCnpj, key: 'cpfOrCnpj') // user.cpfOrCnpj is nullable
/// .validCpfOrCnpj();
///
///
/// String format args:
/// - **{PropertyName}**: The name of the property.
///
SimpleValidationBuilder<String?> validCPFOrCNPJ(
{String? message, String? code}) {
return use((value, entity) {
if (value != null && (_validateCPF(value) || _validateCNPJ(value))) {
return null;
}

if (value != null && value.length == 11) {
final currentCode = code ?? Language.code.validCpfOrCnpj;
final currentMessage = LucidValidation.global.languageManager.translate(
currentCode,
parameters: {
'PropertyName': label.isNotEmpty ? label : key,
},
defaultMessage: message,
);

return ValidationException(
message: currentMessage,
code: currentCode,
key: key,
);
} else if (value != null && value.length == 14) {
final currentCode = code ?? Language.code.validCpfOrCnpj;
final currentMessage = LucidValidation.global.languageManager.translate(
currentCode,
parameters: {
'PropertyName': label.isNotEmpty ? label : key,
},
defaultMessage: message,
);

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

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

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

extension ValidCpfOrCnpjOrNullableValidation
on SimpleValidationBuilder<String?> {
/// Adds a validation rule that checks if the [String?] is a valid CEP (Brazilian postal code) or [null].
///
/// This method verifies that the CEP is in the correct format (#####-###) and consists
/// of only numbers.
///
/// [message] is the error message returned if the validation fails. Defaults to "Invalid CEP".
/// [code] is an optional error code for translation purposes.
///
/// Returns the [LucidValidationBuilder] to allow for method chaining.
///
/// Example:
/// ```dart
/// ...
/// ruleFor((user) => user.cep, key: 'cep')
/// .validCEPOrNull();
/// ```
///
/// String format args:
/// - **{PropertyName}**: The name of the property.
///
SimpleValidationBuilder<String?> validCPFOrCNPJOrNull(
{String? message, String? code}) {
return use((value, entity) {
if (value == null) return null;
if (_validateCPF(value) || _validateCNPJ(value)) return null;

if (value.length == 11) {
final currentCode = code ?? Language.code.validCpfOrCnpj;
final currentMessage = LucidValidation.global.languageManager.translate(
currentCode,
parameters: {
'PropertyName': label.isNotEmpty ? label : key,
},
defaultMessage: message,
);

return ValidationException(
message: currentMessage,
code: currentCode,
key: key,
);
} else if (value.length == 14) {
final currentCode = code ?? Language.code.validCpfOrCnpj;
final currentMessage = LucidValidation.global.languageManager.translate(
currentCode,
parameters: {
'PropertyName': label.isNotEmpty ? label : key,
},
defaultMessage: message,
);

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

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

return ValidationException(
message: currentMessage,
code: currentCode,
key: key,
);
});
}
}
1 change: 1 addition & 0 deletions lib/src/validations/validations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ part 'valid_cnpj_validation.dart';
part 'valid_cpf_validation.dart';
part 'valid_creditcard_validation.dart';
part 'valid_email_validation.dart';
part 'valid_cpf_or_cnpj_validation.dart';
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: "A Dart/Flutter package for building strongly typed validation rule
repository: https://github.com/Flutterando/lucid_validation
homepage: https://pub.dev/documentation/lucid_validation/latest/

version: 1.2.1
version: 1.2.2

environment:
sdk: ">=3.0.0 <4.0.0"
Expand Down
Loading

0 comments on commit 57778e7

Please sign in to comment.