From 7101bda50737caf963318861416024710537ed15 Mon Sep 17 00:00:00 2001 From: Adam Simon Date: Thu, 7 Sep 2023 11:45:35 +0200 Subject: [PATCH] Improve model and evaluation of conditions --- .../Evaluation/EvaluateLogHelper.cs | 9 +-- .../Evaluation/RolloutEvaluator.cs | 12 ++-- .../Models/ComparisonCondition.cs | 2 +- src/ConfigCatClient/Models/Condition.cs | 55 ++----------------- .../Models/ConditionContainer.cs | 54 ++++++++++++++++++ .../Models/PrerequisiteFlagCondition.cs | 2 +- .../Models/SegmentCondition.cs | 2 +- src/ConfigCatClient/Models/TargetingRule.cs | 6 +- 8 files changed, 77 insertions(+), 65 deletions(-) create mode 100644 src/ConfigCatClient/Models/ConditionContainer.cs diff --git a/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs b/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs index ec163b18..d38e6052 100644 --- a/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs +++ b/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs @@ -136,7 +136,8 @@ public static IndentedTextBuilder AppendConditionConsequence(this IndentedTextBu return result ? builder : builder.Append(", skipping the remaining AND conditions"); } - private static IndentedTextBuilder AppendConditions(this IndentedTextBuilder builder, TCondition[] conditions, Func getCondition) + private static IndentedTextBuilder AppendConditions(this IndentedTextBuilder builder, TCondition[] conditions) + where TCondition : IConditionProvider { for (var i = 0; i < conditions.Length; i++) { @@ -147,7 +148,7 @@ private static IndentedTextBuilder AppendConditions(this IndentedTex builder.NewLine("AND "); } - _ = getCondition(conditions[i]) switch + _ = conditions[i].GetCondition(throwIfInvalid: false) switch { ComparisonCondition comparisonCondition => builder.AppendComparisonCondition(comparisonCondition), PrerequisiteFlagCondition prerequisiteFlagCondition => builder.AppendPrerequisiteFlagCondition(prerequisiteFlagCondition), @@ -229,7 +230,7 @@ public static IndentedTextBuilder AppendTargetingRule(this IndentedTextBuilder b var conditions = targetingRule.Conditions; return builder.Append("IF ") - .AppendConditions(conditions, static condition => condition.GetCondition(throwIfInvalid: false)) + .AppendConditions(conditions) .AppendTargetingRuleThenPart(targetingRule, newLine: true, appendPercentageOptions: true, percentageOptionsAttribute); } @@ -287,7 +288,7 @@ public static IndentedTextBuilder AppendSetting(this IndentedTextBuilder builder public static IndentedTextBuilder AppendSegment(this IndentedTextBuilder builder, Segment segment) { - return builder.AppendConditions(segment.Conditions, static condition => condition); + return builder.AppendConditions(segment.Conditions); } public static string ToDisplayText(this Comparator comparator) diff --git a/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs b/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs index d15fa592..bad8365e 100644 --- a/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs +++ b/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs @@ -96,7 +96,7 @@ private bool TryEvaluateTargetingRules(TargetingRule[] targetingRules, ref Evalu var targetingRule = targetingRules[i]; var conditions = targetingRule.Conditions; - if (!TryEvaluateConditions(conditions, static condition => condition.GetCondition()!, targetingRule, contextSalt: context.Key, ref context, out var isMatch)) + if (!TryEvaluateConditions(conditions, targetingRule, contextSalt: context.Key, ref context, out var isMatch)) { logBuilder? .IncreaseIndent() @@ -216,8 +216,8 @@ private bool TryEvaluatePercentageOptions(PercentageOption[] percentageOptions, throw new InvalidOperationException("Sum of percentage option percentages are less than 100)."); } - private bool TryEvaluateConditions(TCondition[] conditions, Func getCondition, TargetingRule? targetingRule, - string contextSalt, ref EvaluateContext context, out bool result) + private bool TryEvaluateConditions(TCondition[] conditions, TargetingRule? targetingRule, string contextSalt, ref EvaluateContext context, out bool result) + where TCondition : IConditionProvider { result = true; @@ -229,7 +229,7 @@ private bool TryEvaluateConditions(TCondition[] conditions, Func(TCondition[] conditions, Func 1) @@ -682,7 +682,7 @@ private bool EvaluateSegmentCondition(SegmentCondition condition, ref EvaluateCo .IncreaseIndent() .NewLine().Append($"Evaluating segment '{segment.Name}':"); - TryEvaluateConditions(segment.Conditions, static condition => condition, targetingRule: null, contextSalt: segment.Name, ref context, out var segmentResult); + TryEvaluateConditions(segment.Conditions, targetingRule: null, contextSalt: segment.Name, ref context, out var segmentResult); var comparator = condition.Comparator; var result = comparator switch diff --git a/src/ConfigCatClient/Models/ComparisonCondition.cs b/src/ConfigCatClient/Models/ComparisonCondition.cs index 1fb387e7..cad62623 100644 --- a/src/ConfigCatClient/Models/ComparisonCondition.cs +++ b/src/ConfigCatClient/Models/ComparisonCondition.cs @@ -33,7 +33,7 @@ public interface IComparisonCondition : ICondition object ComparisonValue { get; } } -internal sealed class ComparisonCondition : IComparisonCondition +internal sealed class ComparisonCondition : Condition, IComparisonCondition { public const Comparator UnknownComparator = (Comparator)byte.MaxValue; diff --git a/src/ConfigCatClient/Models/Condition.cs b/src/ConfigCatClient/Models/Condition.cs index 58b10ba0..3f5a2f24 100644 --- a/src/ConfigCatClient/Models/Condition.cs +++ b/src/ConfigCatClient/Models/Condition.cs @@ -1,12 +1,3 @@ -using System; -using ConfigCat.Client.Utils; - -#if USE_NEWTONSOFT_JSON -using Newtonsoft.Json; -#else -using System.Text.Json.Serialization; -#endif - namespace ConfigCat.Client; /// @@ -14,46 +5,12 @@ namespace ConfigCat.Client; /// public interface ICondition { } -internal struct ConditionWrapper +internal interface IConditionProvider { - private object? condition; - -#if USE_NEWTONSOFT_JSON - [JsonProperty(PropertyName = "t")] -#else - [JsonPropertyName("t")] -#endif - public ComparisonCondition? ComparisonCondition - { - readonly get => this.condition as ComparisonCondition; - set => ModelHelper.SetOneOf(ref this.condition, value); - } - -#if USE_NEWTONSOFT_JSON - [JsonProperty(PropertyName = "s")] -#else - [JsonPropertyName("s")] -#endif - public SegmentCondition? SegmentCondition - { - readonly get => this.condition as SegmentCondition; - set => ModelHelper.SetOneOf(ref this.condition, value); - } - -#if USE_NEWTONSOFT_JSON - [JsonProperty(PropertyName = "d")] -#else - [JsonPropertyName("d")] -#endif - public PrerequisiteFlagCondition? PrerequisiteFlagCondition - { - readonly get => this.condition as PrerequisiteFlagCondition; - set => ModelHelper.SetOneOf(ref this.condition, value); - } + Condition? GetCondition(bool throwIfInvalid = true); +} - public readonly ICondition? GetCondition(bool throwIfInvalid = true) - { - return this.condition as ICondition - ?? (!throwIfInvalid ? null : throw new InvalidOperationException("Condition is missing or invalid.")); - } +internal abstract class Condition : ICondition, IConditionProvider +{ + public Condition? GetCondition(bool throwIfInvalid = true) => this; } diff --git a/src/ConfigCatClient/Models/ConditionContainer.cs b/src/ConfigCatClient/Models/ConditionContainer.cs new file mode 100644 index 00000000..80d1315e --- /dev/null +++ b/src/ConfigCatClient/Models/ConditionContainer.cs @@ -0,0 +1,54 @@ +using System; +using ConfigCat.Client.Utils; + +#if USE_NEWTONSOFT_JSON +using Newtonsoft.Json; +#else +using System.Text.Json.Serialization; +#endif + +namespace ConfigCat.Client; + +internal struct ConditionContainer : IConditionProvider +{ + private object? condition; + +#if USE_NEWTONSOFT_JSON + [JsonProperty(PropertyName = "t")] +#else + [JsonPropertyName("t")] +#endif + public ComparisonCondition? ComparisonCondition + { + readonly get => this.condition as ComparisonCondition; + set => ModelHelper.SetOneOf(ref this.condition, value); + } + +#if USE_NEWTONSOFT_JSON + [JsonProperty(PropertyName = "s")] +#else + [JsonPropertyName("s")] +#endif + public SegmentCondition? SegmentCondition + { + readonly get => this.condition as SegmentCondition; + set => ModelHelper.SetOneOf(ref this.condition, value); + } + +#if USE_NEWTONSOFT_JSON + [JsonProperty(PropertyName = "d")] +#else + [JsonPropertyName("d")] +#endif + public PrerequisiteFlagCondition? PrerequisiteFlagCondition + { + readonly get => this.condition as PrerequisiteFlagCondition; + set => ModelHelper.SetOneOf(ref this.condition, value); + } + + public readonly Condition? GetCondition(bool throwIfInvalid = true) + { + return this.condition as Condition + ?? (!throwIfInvalid ? null : throw new InvalidOperationException("Condition is missing or invalid.")); + } +} diff --git a/src/ConfigCatClient/Models/PrerequisiteFlagCondition.cs b/src/ConfigCatClient/Models/PrerequisiteFlagCondition.cs index 8d18e2db..7ab4515e 100644 --- a/src/ConfigCatClient/Models/PrerequisiteFlagCondition.cs +++ b/src/ConfigCatClient/Models/PrerequisiteFlagCondition.cs @@ -31,7 +31,7 @@ public interface IPrerequisiteFlagCondition : ICondition object ComparisonValue { get; } } -internal sealed class PrerequisiteFlagCondition : IPrerequisiteFlagCondition +internal sealed class PrerequisiteFlagCondition : Condition, IPrerequisiteFlagCondition { public const PrerequisiteFlagComparator UnknownComparator = (PrerequisiteFlagComparator)byte.MaxValue; diff --git a/src/ConfigCatClient/Models/SegmentCondition.cs b/src/ConfigCatClient/Models/SegmentCondition.cs index a3dca1b8..5cd61ed2 100644 --- a/src/ConfigCatClient/Models/SegmentCondition.cs +++ b/src/ConfigCatClient/Models/SegmentCondition.cs @@ -26,7 +26,7 @@ public interface ISegmentCondition : ICondition SegmentComparator Comparator { get; } } -internal sealed class SegmentCondition : ISegmentCondition +internal sealed class SegmentCondition : Condition, ISegmentCondition { public const SegmentComparator UnknownComparator = (SegmentComparator)byte.MaxValue; diff --git a/src/ConfigCatClient/Models/TargetingRule.cs b/src/ConfigCatClient/Models/TargetingRule.cs index 8ff47f8a..b72146a9 100644 --- a/src/ConfigCatClient/Models/TargetingRule.cs +++ b/src/ConfigCatClient/Models/TargetingRule.cs @@ -36,7 +36,7 @@ public interface ITargetingRule internal sealed class TargetingRule : ITargetingRule { - private ConditionWrapper[]? conditions; + private ConditionContainer[]? conditions; #if USE_NEWTONSOFT_JSON [JsonProperty(PropertyName = "c")] @@ -44,9 +44,9 @@ internal sealed class TargetingRule : ITargetingRule [JsonPropertyName("c")] #endif [NotNull] - public ConditionWrapper[]? Conditions + public ConditionContainer[]? Conditions { - get => this.conditions ?? ArrayUtils.EmptyArray(); + get => this.conditions ?? ArrayUtils.EmptyArray(); set => this.conditions = value; }