namespace ErsatzTV.Core.Domain; /// /// Bounds for a multi-collection member's per-source Weight (#70). /// /// The floor is 1, not 0. A weight is a share of airtime, and 0 has no meaning on that scale — /// "don't play this source" is expressed by removing it from the multi collection, not by weighting it to /// nothing. Rejecting it here keeps that intent from being expressed in a way the rotation would have to /// interpret. WeightedShuffleCollectionEnumerator independently clamps to this range rather than /// trusting it, so a row that predates this gate rotates at the floor instead of vanishing; the two are /// belt-and-braces, not duplicates — neither is redundant. /// /// /// The ceiling is the same argument as the floor, not an arithmetic guard. A weight of a billion is not a /// share of airtime any more than 0 is, so it is refused at the boundary rather than silently reinterpreted. /// The rotation arithmetic is made safe by EffectiveWeight's clamp (and by CycleLength summing /// to long) — not by this constant: with the clamp in place a sum cannot overflow whatever the /// stored value is, which is exactly why a row that predates this gate is still safe. /// An_Enormous_Weight_Does_Not_Overflow_The_Rotation pins that. /// /// public static class MultiCollectionItemWeight { public const int Minimum = 1; public const int Maximum = 1000; public const int Default = 1; public static bool IsValid(int weight) => weight is >= Minimum and <= Maximum; public static string ValidationMessage => $"Weight must be between {Minimum} and {Maximum}"; }