<commit_before>﻿using System;
using System.Collections.Generic;
using System.Reflection;
using AtleX.CommandLineArguments.Validators;

namespace AtleX.CommandLineArguments.Parsers.Helpers
{
  /// <summary>
  /// Represents a helper for validation of command line arguments
  /// </summary>
  internal sealed class ValidationHelper
  {
    /// <summary>
    /// Gets the <see cref="IEnumerable{T}"/> of <see cref="ArgumentValidator"/> to validate the arguments with
    /// </summary>
    private readonly IEnumerable<ArgumentValidator> argumentValidators;

    /// <summary>
    /// Initializes a new instance of <see cref="ValidationHelper"/> with the
    /// specified <see cref="IEnumerable{T}"/> of <see cref="ArgumentValidator"/>
    /// </summary>
    /// <param name="validatorsToRun">
    /// The <see cref="IEnumerable{T}"/> of <see cref="ArgumentValidator"/> to validate with
    /// </param>
    public ValidationHelper(IEnumerable<ArgumentValidator> validatorsToRun)
    {
      this.argumentValidators = validatorsToRun ?? throw new ArgumentNullException(nameof(validatorsToRun));
    }

    /// <summary>
    /// Try validating the argument with all argument validators
    /// </summary>
    /// <param name="parsedPropertyToValidate">
    /// The <see cref="PropertyInfo"/> of the argument to validate
    /// </param>
    /// <param name="isSpecified">
    /// True when the argument was specified on the command line, false otherwise
    /// </param>
    /// <param name="originalValue">
    /// The value as originally specified on the command line, if any
    /// </param>
    /// <param name="validationErrors">
    /// When validation fails, the value is an <see cref="IEnumerable{T}"/> of
    /// <see cref="ValidationError"/> with all validation errors. Otherwise, the
    /// value is an empty <see cref="IEnumerable{T}"/> of <see cref="ValidationError"/>
    /// </param>
    /// <returns>
    /// True when the argument is valid, false otherwise
    /// </returns>
    public bool TryValidate(PropertyInfo parsedPropertyToValidate, bool isSpecified, string originalValue, out IEnumerable<ValidationError> validationErrors)
    {
      _ = parsedPropertyToValidate ?? throw new ArgumentNullException(nameof(parsedPropertyToValidate));

      var result = true;

      var currentValidationErrors =  new List<ValidationError>();

      foreach (var currentValidator in this.argumentValidators)
      {
        var isValid = currentValidator.TryValidate(parsedPropertyToValidate, isSpecified, originalValue, out ValidationError validationError);

        if (!isValid)
        {
          currentValidationErrors.Add(validationError);
          result = false;
        }
      }

      validationErrors = currentValidationErrors;

      return result;
    }
  }
}
<commit_msg>Fixed incorrectly allocating a List<ValidationError>even when no validation errors were found (fixes #34)<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AtleX.CommandLineArguments.Validators;

namespace AtleX.CommandLineArguments.Parsers.Helpers
{
  /// <summary>
  /// Represents a helper for validation of command line arguments
  /// </summary>
  internal sealed class ValidationHelper
  {
    /// <summary>
    /// Gets the <see cref="IEnumerable{T}"/> of <see cref="ArgumentValidator"/> to validate the arguments with
    /// </summary>
    private readonly IEnumerable<ArgumentValidator> argumentValidators;

    /// <summary>
    /// Initializes a new instance of <see cref="ValidationHelper"/> with the
    /// specified <see cref="IEnumerable{T}"/> of <see cref="ArgumentValidator"/>
    /// </summary>
    /// <param name="validatorsToRun">
    /// The <see cref="IEnumerable{T}"/> of <see cref="ArgumentValidator"/> to validate with
    /// </param>
    public ValidationHelper(IEnumerable<ArgumentValidator> validatorsToRun)
    {
      this.argumentValidators = validatorsToRun ?? throw new ArgumentNullException(nameof(validatorsToRun));
    }

    /// <summary>
    /// Try validating the argument with all argument validators
    /// </summary>
    /// <param name="parsedPropertyToValidate">
    /// The <see cref="PropertyInfo"/> of the argument to validate
    /// </param>
    /// <param name="isSpecified">
    /// True when the argument was specified on the command line, false otherwise
    /// </param>
    /// <param name="originalValue">
    /// The value as originally specified on the command line, if any
    /// </param>
    /// <param name="validationErrors">
    /// When validation fails, the value is an <see cref="IEnumerable{T}"/> of
    /// <see cref="ValidationError"/> with all validation errors. Otherwise, the
    /// value is an empty <see cref="IEnumerable{T}"/> of <see cref="ValidationError"/>
    /// </param>
    /// <returns>
    /// True when the argument is valid, false otherwise
    /// </returns>
    public bool TryValidate(PropertyInfo parsedPropertyToValidate, bool isSpecified, string originalValue, out IEnumerable<ValidationError> validationErrors)
    {
      _ = parsedPropertyToValidate ?? throw new ArgumentNullException(nameof(parsedPropertyToValidate));

      var result = true;

      List<ValidationError> currentValidationErrors =  null; // PERF Only set the collection of validation errors when there's actually one or more

      foreach (var currentValidator in this.argumentValidators)
      {
        var isValid = currentValidator.TryValidate(parsedPropertyToValidate, isSpecified, originalValue, out ValidationError validationError);

        if (!isValid)
        {
          if (currentValidationErrors == null)
          {
            currentValidationErrors = new List<ValidationError>();
          }

          currentValidationErrors.Add(validationError);
          result = false;
        }
      }

      if (currentValidationErrors != null)
      {
        validationErrors = currentValidationErrors;
      }
      else
      {
        // Never return a null IEnumerable<T> because people want to use LINQ stuff on it
        validationErrors = Enumerable.Empty<ValidationError>(); 
      }

      return result;
    }
  }
}

{'fraction_non_alphanumeric': 0.11708502024291498, 'fraction_numerical': 0.00032388663967611336, 'mean_word_length': 3.0418848167539267, 'pattern_counts': {'":': 0, '<': 79, '<?xml version=': 0, '>': 79, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5451815', 'n_tokens_mistral': 1563, 'n_tokens_neox': 1557, 'n_words': 481}