using System; using System.Collections; using System.Linq; using Chernobyl.Collections.Generic; using JetBrains.Annotations; namespace Chernobyl { /// /// Instance used to validate parameters or other values. /// /// The type being validated. public struct Validater { /// /// Constructor. /// /// The value to validate. /// The name of the parameter. This is used /// in the exception message to make the message easier to understand. public Validater(T value, string name) { Value = value; Name = name; } /// /// The value to validate. /// public T Value { get; } /// /// The name of the parameter. This is used in the exception message to /// make the message easier to understand. /// public string Name { get; } } /// /// Validates reference type instances. /// public static class ReferenceValidater { /// /// Throws an if is null. /// /// The instance containing the value to validate. /// The this instance. public static Validater IfNull(this Validater validator) where T : class { if (validator.Value == null) throw new ArgumentNullException(validator.Name, "Argument cannot be null. Please provide a valid value."); return validator; } /// /// Throws an if is null. Does /// the same thing as param.Throw(paramName).IfNull(). /// /// The type of the parameter. This type must be a reference type. /// The instance to check. /// The name of the parameter that caused the exception, if any. /// Thrown if is null. [ContractAnnotation("param:null => halt")] public static Validater ThrowIfNull([NoEnumeration, CanBeNull]this T param, string paramName) where T : class => param.Throw(paramName).IfNull(); } /// /// Validates instances. /// public static class ComparableValidater { /// /// Throws an if is less /// than . /// /// The instance containing the value to validate. /// The value to compare against. /// The name of the value comparing against or an empty string if it has /// no name. /// The this instance. public static Validater IfLessThan(this Validater validator, T lessThan, string name = "") where T : IComparable { if (validator.Value.CompareTo(lessThan) < 0) { var otherName = name.IsEmptyOrNull() ? lessThan.ToString() : $"{name} (${lessThan})"; throw new ArgumentOutOfRangeException(validator.Name, validator.Value, $"{validator.Name} cannot be less than {otherName}."); } return validator; } /// /// Throws an if is less /// than or equal to . /// /// The instance containing the value to validate. /// The value to compare against. /// The name of the value comparing against or an empty string if it has /// no name. /// The this instance. public static Validater IfLessThanOrEqualTo(this Validater validator, T lessThan, string name = "") where T : IComparable { if (validator.Value.CompareTo(lessThan) <= 0) { var otherName = name.IsEmptyOrNull() ? lessThan.ToString() : $"{name} (${lessThan})"; throw new ArgumentOutOfRangeException(validator.Name, validator.Value, $"{validator.Name} cannot be less than or equal to {otherName}."); } return validator; } /// /// Throws an if is greater /// than . /// /// The instance containing the value to validate. /// The value to compare against. /// The name of the value comparing against or an empty string if it has /// no name. /// The this instance. public static Validater IfGreaterThan(this Validater validator, T greaterThan, string name = "") where T : IComparable { if (validator.Value.CompareTo(greaterThan) > 0) { var otherName = name.IsEmptyOrNull() ? greaterThan.ToString() : $"{name} (${greaterThan})"; throw new ArgumentOutOfRangeException(validator.Name, validator.Value, $"{validator.Name} cannot be greater than {otherName}."); } return validator; } /// /// Throws an if is greater /// than or equal to . /// /// The instance containing the value to validate. /// The value to compare against. /// The name of the value comparing against or an empty string if it has /// no name. /// The this instance. public static Validater IfGreaterThanOrEqualTo(this Validater validator, T other, string name = "") where T : IComparable { if (validator.Value.CompareTo(other) >= 0) { var otherName = name.IsEmptyOrNull() ? other.ToString() : $"{name} (${other})"; throw new ArgumentOutOfRangeException(validator.Name, validator.Value, $"{validator.Name} cannot be greater than or equal to {otherName}."); } return validator; } /// /// Throws an if /// is not equal to or between and . /// /// The instance containing the value to validate. /// The inclusive lower limit. /// The inclusive upper limit. /// The this instance. public static Validater IfNotBetween(this Validater validator, T lessThan, T greaterThan) where T : IComparable => validator.IfLessThan(lessThan).IfGreaterThan(greaterThan); } /// /// Validates instances. /// public static class EnumerableValidater { /// /// Throws an exception if the provided contains nothing. /// public static Validater IfEmpty(this Validater validator) where T : IEnumerable { // ReSharper disable PossibleMultipleEnumeration if (!validator.Value.Cast().Any()) throw new ArgumentException($"{validator.Name} cannot be empty.", validator.Name); return validator; // ReSharper restore PossibleMultipleEnumeration } /// /// Throws an if is null or empty. /// /// The instance containing the value to validate. /// The this instance. public static Validater IfNullOrEmpty(this Validater validator) where T : class, IEnumerable => validator.IfNull().IfEmpty(); /// /// Throws an if is null. Does /// the same thing as param.Throw(paramName).IfNullOrEmpty(). /// /// The type of the parameter. This type must be a reference type. /// The instance to check. /// The name of the parameter that caused the exception, if any. /// Thrown if is null. [ContractAnnotation("param:null => halt")] public static Validater ThrowIfEmptyOrNull([NoEnumeration, CanBeNull]this T param, string paramName) where T : class, IEnumerable => param.Throw(paramName).IfNullOrEmpty(); } /// /// Methods for easier validation of parameters. /// public static class Validater { /// /// Creates the from the provided. /// /// The type being validated. /// The value to validate. /// The name of the parameter. This is used /// in the exception message to make the message easier to understand. public static Validater Throw(this T value, string name) => new Validater(value, name); } }