asyncApplyValidator function

Future<ValidationResult> asyncApplyValidator(
  1. Validator validator,
  2. Map data,
  3. Angel? app
)

Asynchronously apply a validator, running any AngelMatchers, also those of nested Validators.

Like Validator.check, matchers are given the input, not the filtered data, as their context, and an AngelMatcher does not match null. Throws a StateError if app is null and the validator has an AngelMatcher.

Implementation

Future<ValidationResult> asyncApplyValidator(
  Validator validator,
  Map data,
  Angel? app,
) async {
  if (app == null && _hasAngelMatcher(validator)) {
    throw StateError(
      'The validator has an AngelMatcher, which needs an Angel app, but none '
      'was given.',
    );
  }

  var result = validator.check(data);
  if (result.errors.isNotEmpty) return result;

  // The input, with the default values that [Validator.check] added.
  var context = {...result.data, ...data};
  var errors = <String>[];
  var m = Map<String, dynamic>.from(result.data);

  for (var key in result.data.keys) {
    var value = result.data[key];
    var input = context[key];
    var description = StringDescription("'$key': expected ");
    var valid = true;

    for (var rule in validator.rules[key]!) {
      if (rule is Validator && input is Map && _hasAngelMatcher(rule)) {
        var nested = await asyncApplyValidator(rule, input, app);

        if (nested.errors.isNotEmpty) {
          errors.addAll(nested.errors);
          valid = false;
          break;
        }

        // Several validators of one key keep the data of each.
        m[key] = {...m[key] as Map, ...nested.data};
      } else if (rule is AngelMatcher) {
        // [Validator.check] has refused `null` for an [AngelMatcher].
        if (!await rule.matchesWithAngel(
          value as Object,
          key,
          context,
          {},
          app!,
        )) {
          if (!validator.customErrorMessages.containsKey(key)) {
            errors.add(rule.describe(description).toString().trim());
          }
          valid = false;
          break;
        }
      }
    }

    if (!valid) {
      m.remove(key);
      if (validator.customErrorMessages.containsKey(key)) {
        errors.add(validator.customError(key, value));
      }
    }
  }

  return result.withData(m).withErrors(errors);
}