diff --git a/Dummies/AnyCollection.cs b/Dummies/AnyCollection.cs index 76b0dee2..ff2e2cd2 100644 --- a/Dummies/AnyCollection.cs +++ b/Dummies/AnyCollection.cs @@ -1,9 +1,3 @@ -#region Usings declarations - -using System.Globalization; - -#endregion - namespace Dummies; /// @@ -25,20 +19,6 @@ namespace Dummies; public abstract class AnyCollection : IAny, IHasRandomSource where TSelf : AnyCollection { - #region Statics members declarations - - private static string V(int value) { - return value.ToString(CultureInfo.InvariantCulture); - } - - private static int RequireNonNegative(int count, string parameterName) { - if (count < 0) { throw new ArgumentOutOfRangeException(parameterName, count, "The count must not be negative."); } - - return count; - } - - #endregion - #region Fields declarations private protected readonly RandomSource? SourceOrNull; @@ -57,14 +37,14 @@ private protected AnyCollection(RandomSource? source, CollectionState sta /// A new generator carrying the added constraint. /// Thrown when the constraint contradicts a constraint already declared. public TSelf NonEmpty() { - return With(State.WithMinCount(1, "NonEmpty()")); + return With(CountConstraints.NonEmpty(State)); } /// Fixes the collection to no elements. /// A new generator carrying the added constraint. /// Thrown when the constraint contradicts a constraint already declared. public TSelf Empty() { - return With(State.WithExactCount(0, "Empty()")); + return With(CountConstraints.Empty(State)); } /// Fixes the exact number of elements. Declared once per generator. @@ -73,9 +53,7 @@ public TSelf Empty() { /// Thrown when is negative. /// Thrown when the constraint contradicts a constraint already declared. public TSelf WithCount(int count) { - RequireNonNegative(count, nameof(count)); - - return With(State.WithExactCount(count, $"WithCount({V(count)})")); + return With(CountConstraints.WithCount(State, count)); } /// Requires at least elements. @@ -84,9 +62,7 @@ public TSelf WithCount(int count) { /// Thrown when is negative. /// Thrown when the constraint contradicts a constraint already declared. public TSelf WithMinCount(int count) { - RequireNonNegative(count, nameof(count)); - - return With(State.WithMinCount(count, $"WithMinCount({V(count)})")); + return With(CountConstraints.WithMinCount(State, count)); } /// Requires at most elements. @@ -95,9 +71,7 @@ public TSelf WithMinCount(int count) { /// Thrown when is negative. /// Thrown when the constraint contradicts a constraint already declared. public TSelf WithMaxCount(int count) { - RequireNonNegative(count, nameof(count)); - - return With(State.WithMaxCount(count, $"WithMaxCount({V(count)})")); + return With(CountConstraints.WithMaxCount(State, count)); } /// Requires a number of elements within the inclusive range [, ]. @@ -108,13 +82,7 @@ public TSelf WithMaxCount(int count) { /// Thrown when is greater than . /// Thrown when the constraint contradicts a constraint already declared. public TSelf WithCountBetween(int minimum, int maximum) { - RequireNonNegative(minimum, nameof(minimum)); - RequireNonNegative(maximum, nameof(maximum)); - if (minimum > maximum) { throw new ArgumentException($"The minimum ({V(minimum)}) must be less than or equal to the maximum ({V(maximum)}).", nameof(minimum)); } - - string constraint = $"WithCountBetween({V(minimum)}, {V(maximum)})"; - - return With(State.WithMinCount(minimum, constraint).WithMaxCount(maximum, constraint)); + return With(CountConstraints.WithCountBetween(State, minimum, maximum)); } /// diff --git a/Dummies/AnyDictionary.cs b/Dummies/AnyDictionary.cs index 1ddf499d..c2f16240 100644 --- a/Dummies/AnyDictionary.cs +++ b/Dummies/AnyDictionary.cs @@ -1,9 +1,3 @@ -#region Usings declarations - -using System.Globalization; - -#endregion - namespace Dummies; /// @@ -18,20 +12,6 @@ namespace Dummies; public sealed class AnyDictionary : IAny>, IHasRandomSource where TKey : notnull { - #region Statics members declarations - - private static string V(int value) { - return value.ToString(CultureInfo.InvariantCulture); - } - - private static int RequireNonNegative(int count, string parameterName) { - if (count < 0) { throw new ArgumentOutOfRangeException(parameterName, count, "The count must not be negative."); } - - return count; - } - - #endregion - #region Fields declarations private readonly CollectionState _keys; @@ -52,14 +32,14 @@ internal AnyDictionary(RandomSource? source, CollectionState keys, IAnyA new generator carrying the added constraint. /// Thrown when the constraint contradicts a constraint already declared. public AnyDictionary NonEmpty() { - return With(_keys.WithMinCount(1, "NonEmpty()")); + return With(CountConstraints.NonEmpty(_keys)); } /// Fixes the dictionary to no entries. /// A new generator carrying the added constraint. /// Thrown when the constraint contradicts a constraint already declared. public AnyDictionary Empty() { - return With(_keys.WithExactCount(0, "Empty()")); + return With(CountConstraints.Empty(_keys)); } /// Fixes the exact number of entries. Declared once per generator. @@ -68,9 +48,7 @@ public AnyDictionary Empty() { /// Thrown when is negative. /// Thrown when the constraint contradicts a constraint already declared. public AnyDictionary WithCount(int count) { - RequireNonNegative(count, nameof(count)); - - return With(_keys.WithExactCount(count, $"WithCount({V(count)})")); + return With(CountConstraints.WithCount(_keys, count)); } /// Requires at least entries. @@ -79,9 +57,7 @@ public AnyDictionary WithCount(int count) { /// Thrown when is negative. /// Thrown when the constraint contradicts a constraint already declared. public AnyDictionary WithMinCount(int count) { - RequireNonNegative(count, nameof(count)); - - return With(_keys.WithMinCount(count, $"WithMinCount({V(count)})")); + return With(CountConstraints.WithMinCount(_keys, count)); } /// Requires at most entries. @@ -90,9 +66,7 @@ public AnyDictionary WithMinCount(int count) { /// Thrown when is negative. /// Thrown when the constraint contradicts a constraint already declared. public AnyDictionary WithMaxCount(int count) { - RequireNonNegative(count, nameof(count)); - - return With(_keys.WithMaxCount(count, $"WithMaxCount({V(count)})")); + return With(CountConstraints.WithMaxCount(_keys, count)); } /// Requires a number of entries within the inclusive range [, ]. @@ -103,13 +77,7 @@ public AnyDictionary WithMaxCount(int count) { /// Thrown when is greater than . /// Thrown when the constraint contradicts a constraint already declared. public AnyDictionary WithCountBetween(int minimum, int maximum) { - RequireNonNegative(minimum, nameof(minimum)); - RequireNonNegative(maximum, nameof(maximum)); - if (minimum > maximum) { throw new ArgumentException($"The minimum ({V(minimum)}) must be less than or equal to the maximum ({V(maximum)}).", nameof(minimum)); } - - string constraint = $"WithCountBetween({V(minimum)}, {V(maximum)})"; - - return With(_keys.WithMinCount(minimum, constraint).WithMaxCount(maximum, constraint)); + return With(CountConstraints.WithCountBetween(_keys, minimum, maximum)); } /// diff --git a/Dummies/CountConstraints.cs b/Dummies/CountConstraints.cs new file mode 100644 index 00000000..ed7f8232 --- /dev/null +++ b/Dummies/CountConstraints.cs @@ -0,0 +1,82 @@ +#region Usings declarations + +using System.Globalization; + +#endregion + +namespace Dummies; + +/// +/// The count-constraint facade shared by every collection generator, defined once over +/// . The builders +/// (a list, an array, a sequence, a set) and — whose keys run through +/// the very same — each expose NonEmpty/Empty/WithCount/ +/// WithMinCount/WithMaxCount/WithCountBetween by delegating here, so the argument +/// validation and the constraint labels that surface in a live +/// in exactly one place and cannot drift between the two surfaces. +/// +/// +/// Each method takes a state and returns the tightened state; the caller wraps that state back into its own +/// immutable generator. The label strings ("NonEmpty()", "WithCount(3)", ...) are part of the +/// user-facing conflict messages, so they are produced here rather than in the label-agnostic +/// , which only records whichever label its caller hands it. +/// +internal static class CountConstraints { + + #region Statics members declarations + + private static string V(int value) { + return value.ToString(CultureInfo.InvariantCulture); + } + + private static int RequireNonNegative(int count, string parameterName) { + if (count < 0) { throw new ArgumentOutOfRangeException(parameterName, count, "The count must not be negative."); } + + return count; + } + + #endregion + + /// Requires at least one element. + internal static CollectionState NonEmpty(CollectionState state) { + return state.WithMinCount(1, "NonEmpty()"); + } + + /// Fixes the collection to no elements. + internal static CollectionState Empty(CollectionState state) { + return state.WithExactCount(0, "Empty()"); + } + + /// Fixes the exact number of elements. + internal static CollectionState WithCount(CollectionState state, int count) { + RequireNonNegative(count, nameof(count)); + + return state.WithExactCount(count, $"WithCount({V(count)})"); + } + + /// Requires at least elements. + internal static CollectionState WithMinCount(CollectionState state, int count) { + RequireNonNegative(count, nameof(count)); + + return state.WithMinCount(count, $"WithMinCount({V(count)})"); + } + + /// Requires at most elements. + internal static CollectionState WithMaxCount(CollectionState state, int count) { + RequireNonNegative(count, nameof(count)); + + return state.WithMaxCount(count, $"WithMaxCount({V(count)})"); + } + + /// Requires a number of elements within the inclusive range [, ]. + internal static CollectionState WithCountBetween(CollectionState state, int minimum, int maximum) { + RequireNonNegative(minimum, nameof(minimum)); + RequireNonNegative(maximum, nameof(maximum)); + if (minimum > maximum) { throw new ArgumentException($"The minimum ({V(minimum)}) must be less than or equal to the maximum ({V(maximum)}).", nameof(minimum)); } + + string constraint = $"WithCountBetween({V(minimum)}, {V(maximum)})"; + + return state.WithMinCount(minimum, constraint).WithMaxCount(maximum, constraint); + } + +}