Skip to content

Commit

Permalink
Merge pull request #303 from gregsdennis/logic/access-to-rules
Browse files Browse the repository at this point in the history
make rules public (ctors still internal)
  • Loading branch information
gregsdennis committed Jul 22, 2022
2 parents 1c69367 + c505eaf commit b974805
Show file tree
Hide file tree
Showing 48 changed files with 1,415 additions and 183 deletions.
9 changes: 5 additions & 4 deletions Json.More/Json.More.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
<LangVersion>latest</LangVersion>
<DocumentationFile>Json.More.xml</DocumentationFile>
<PackageIcon>json-logo-256.png</PackageIcon>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../json-everything.snk</AssemblyOriginatorKeyFile>
<Nullable>enable</Nullable>
</PropertyGroup>
<NoWarn>CS1570</NoWarn>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" Version="6.0.2" />
Expand Down
4 changes: 2 additions & 2 deletions JsonLogic.Tests/AddTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ public void AddSingleFalseThrowsError()
}

[Test]
public void AddSingleNullThrowsError()
public void AddSingleNullReturns0()
{
var rule = new AddRule(LiteralRule.Null);

Assert.Throws<JsonLogicException>(() => rule.Apply());
JsonAssert.AreEquivalent(0, rule.Apply());
}
}
14 changes: 12 additions & 2 deletions JsonLogic.Tests/GithubTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using Json.Logic.Rules;
using Json.More;
using NUnit.Framework;

Expand Down Expand Up @@ -63,7 +64,7 @@ public void Issue183_RuleEvaluatesWrong2_Falsy()
{
var jsonRule = "{\"and\":[{\"if\":[{\"var\":\"data.r.0\"},{\"in\":[{\"var\":\"data.r.0.tg\"},[\"140539006\"]]},true]},{\"if\":[{\"var\":\"data.t.0\"},{\"in\":[{\"var\":\"data.t.0.tg\"},[\"140539006\"]]},true]},{\"if\":[{\"var\":\"data.v.0\"},{\"in\":[{\"var\":\"data.v.0.tg\"},[\"140539006\"]]},true]}]}";
var logic = JsonNode.Parse(jsonRule);
var rule = JsonSerializer.Deserialize<Rule>(logic.AsJsonString());
var rule = logic.Deserialize<Rule>();


var data = JsonNode.Parse("{\"data\":{\"r\":[{\"tg\":\"140539006\"}],\"t\":[{\"tg\":\"140539006\"}],\"v\":[{\"tg\":\"Test\"}]}}");
Expand All @@ -78,7 +79,7 @@ public void Issue183_RuleEvaluatesWrong3_Falsy()
{
var jsonRule = "{\"===\":[{\"reduce\":[[{\"var\":\"data.r\"},{\"var\":\"data.t\"},{\"var\":\"data.v\"}],{\"\\u002B\":[{\"var\":\"accumulator\"},{\"if\":[{\"var\":\"current.0\"},1,0]}]},0]},1]}";
var logic = JsonNode.Parse(jsonRule);
var rule = JsonSerializer.Deserialize<Rule>(logic.AsJsonString());
var rule = logic.Deserialize<Rule>();


var data = JsonNode.Parse("{\"data\":{\"r\":[{\"tg\":\"140539006\"},{\"tg\":\"140539006\"}]}}");
Expand Down Expand Up @@ -112,4 +113,13 @@ public void Issue286_InShouldReturnFalseForNonArray()

JsonAssert.IsFalse(result);
}

[Test]
public void Pull303_CustomConverters()
{
var rule = JsonSerializer.Deserialize<Rule>("{ \"+\" : [ 1, 2 ] }");

Assert.IsInstanceOf<AddRule>(rule);
Assert.IsTrue(rule.Apply().IsEquivalentTo(3));
}
}
4 changes: 2 additions & 2 deletions JsonLogic.Tests/LessThanEqualTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ public void LessThanEqualObjectThrowsError()
}

[Test]
public void LessThanEqualNullThrowsError()
public void LessThanEqualNullCastsNullToZero()
{
var rule = new LessThanEqualRule(LiteralRule.Null, 2);

Assert.Throws<JsonLogicException>(() => rule.Apply());
JsonAssert.IsTrue(rule.Apply());
}
[Test]
public void BetweenValueInRangeReturnsTrue()
Expand Down
4 changes: 2 additions & 2 deletions JsonLogic.Tests/LessThanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ public void LessThanObjectThrowsError()
}

[Test]
public void LessThanNullThrowsError()
public void LessThanNullCastsNullToZero()
{
var rule = new LessThanRule(LiteralRule.Null, 2);

Assert.Throws<JsonLogicException>(() => rule.Apply());
JsonAssert.IsTrue(rule.Apply());
}

[Test]
Expand Down
4 changes: 2 additions & 2 deletions JsonLogic.Tests/MoreThanEqualTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ public void MoreThanEqualObjectThrowsError()
}

[Test]
public void MoreThanEqualNullThrowsError()
public void MoreThanEqualNullCastsNullToZero()
{
var rule = new MoreThanEqualRule(LiteralRule.Null, 2);

Assert.Throws<JsonLogicException>(() => rule.Apply());
JsonAssert.IsFalse(rule.Apply());
}
}
4 changes: 2 additions & 2 deletions JsonLogic.Tests/MoreThanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ public void MoreThanObjectThrowsError()
}

[Test]
public void MoreThanNullThrowsError()
public void MoreThanNullCastsNullToZero()
{
var rule = new MoreThanRule(LiteralRule.Null, 2);

Assert.Throws<JsonLogicException>(() => rule.Apply());
JsonAssert.IsFalse(rule.Apply());
}
}
5 changes: 3 additions & 2 deletions JsonLogic/JsonLogic.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
<PackageTags>json logic json-logic jsonlogic</PackageTags>
<PackageReleaseNotes>https://gregsdennis.github.io/json-everything/release-notes/json-logic.html</PackageReleaseNotes>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>3.0.1</Version>
<Version>3.1.0</Version>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.1.0</FileVersion>
<FileVersion>3.1.0.0</FileVersion>
<DocumentationFile>JsonLogic.xml</DocumentationFile>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
Expand All @@ -26,6 +26,7 @@
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../json-everything.snk</AssemblyOriginatorKeyFile>
<Nullable>enable</Nullable>
<NoWarn>CS1570</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions JsonLogic/JsonNodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ public static bool IsTruthy(this JsonNode? node)
/// </returns>
public static decimal? Numberify(this JsonNode? node)
{
if (node is null) return 0;

if (node is not JsonValue value) return null;

if (value.TryGetValue(out string? s)) return decimal.TryParse(s, out var d) ? d : null;
Expand Down
15 changes: 6 additions & 9 deletions JsonLogic/Rule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,23 +81,20 @@ public override Rule Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSer
{
if (reader.TokenType == JsonTokenType.StartObject)
{
var data = JsonSerializer.Deserialize<Dictionary<string, ArgumentCollection>>(ref reader, options)!;
var node = JsonSerializer.Deserialize<JsonNode?>(ref reader, options);

if (data.Count != 1)
throw new JsonException("Rules must contain exactly one operator key with an array of arguments.");
if (node is not JsonObject { Count: 1 } data)
throw new JsonException("Rules must be objects that contain exactly one operator key with an array of arguments.");

var (op, args) = data.First();

var ruleType = RuleRegistry.GetRule(op);
if (ruleType == null)
throw new JsonException($"Cannot identify rule for {op}");

var value = args ?? new ArgumentCollection((Rule?)null);

return (Rule)Activator.CreateInstance(ruleType,
value.Cast<object>()
.Select(o => o ?? new LiteralRule(null))
.ToArray());
return args is null
? (Rule)JsonSerializer.Deserialize("[]", ruleType, options)!
: (Rule)args.Deserialize(ruleType, options)!;
}

if (reader.TokenType == JsonTokenType.StartArray)
Expand Down
47 changes: 43 additions & 4 deletions JsonLogic/Rules/AddRule.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;

namespace Json.Logic.Rules;

/// <summary>
/// Handles the `+` operation.
/// </summary>
[Operator("+")]
internal class AddRule : Rule
[JsonConverter(typeof(AddRuleJsonConverter))]
public class AddRule : Rule
{
private readonly List<Rule> _items;

public AddRule(Rule a, params Rule[] more)
internal AddRule(Rule a, params Rule[] more)
{
_items = new List<Rule> { a };
_items.AddRange(more);
}

/// <summary>
/// Applies the rule to the input data.
/// </summary>
/// <param name="data">The input data.</param>
/// <param name="contextData">
/// Optional secondary data. Used by a few operators to pass a secondary
/// data context to inner operators.
/// </param>
/// <returns>The result of the rule.</returns>
public override JsonNode? Apply(JsonNode? data, JsonNode? contextData = null)
{
decimal result = 0;
Expand All @@ -32,4 +49,26 @@ public AddRule(Rule a, params Rule[] more)

return result;
}
}
}

internal class AddRuleJsonConverter : JsonConverter<AddRule>
{
public override AddRule? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var node = JsonSerializer.Deserialize<JsonNode?>(ref reader, options);

var parameters = node is JsonArray
? node.Deserialize<Rule[]>()
: new[] { node.Deserialize<Rule>()! };

if (parameters == null || parameters.Length == 0)
throw new JsonException("The + rule needs an array of parameters.");

return new AddRule(parameters[0], parameters.Skip(1).ToArray());
}

public override void Write(Utf8JsonWriter writer, AddRule value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
42 changes: 38 additions & 4 deletions JsonLogic/Rules/AllRule.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
using System.Linq;
using System;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;

namespace Json.Logic.Rules;

/// <summary>
/// Handles the `all` operation.
/// </summary>
[Operator("all")]
internal class AllRule : Rule
[JsonConverter(typeof(AllRuleJsonConverter))]
public class AllRule : Rule
{
private readonly Rule _input;
private readonly Rule _rule;

public AllRule(Rule input, Rule rule)
internal AllRule(Rule input, Rule rule)
{
_input = input;
_rule = rule;
}

/// <summary>
/// Applies the rule to the input data.
/// </summary>
/// <param name="data">The input data.</param>
/// <param name="contextData">
/// Optional secondary data. Used by a few operators to pass a secondary
/// data context to inner operators.
/// </param>
/// <returns>The result of the rule.</returns>
public override JsonNode? Apply(JsonNode? data, JsonNode? contextData = null)
{
var input = _input.Apply(data, contextData);
Expand All @@ -26,4 +42,22 @@ public AllRule(Rule input, Rule rule)
return (results.Any() &&
results.All(result => result.IsTruthy()));
}
}
}

internal class AllRuleJsonConverter : JsonConverter<AllRule>
{
public override AllRule? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var parameters = JsonSerializer.Deserialize<Rule[]>(ref reader, options);

if (parameters is not { Length:2})
throw new JsonException("The all rule needs an array with 2 parameters.");

return new AllRule(parameters[0], parameters[1]);
}

public override void Write(Utf8JsonWriter writer, AllRule value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
42 changes: 38 additions & 4 deletions JsonLogic/Rules/AndRule.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;

namespace Json.Logic.Rules;

/// <summary>
/// Handles the `and` operation.
/// </summary>
[Operator("and")]
internal class AndRule : Rule
[JsonConverter(typeof(AndRuleJsonConverter))]
public class AndRule : Rule
{
private readonly List<Rule> _items;

public AndRule(Rule a, params Rule[] more)
internal AndRule(Rule a, params Rule[] more)
{
_items = new List<Rule> { a };
_items.AddRange(more);
}

/// <summary>
/// Applies the rule to the input data.
/// </summary>
/// <param name="data">The input data.</param>
/// <param name="contextData">
/// Optional secondary data. Used by a few operators to pass a secondary
/// data context to inner operators.
/// </param>
/// <returns>The result of the rule.</returns>
public override JsonNode? Apply(JsonNode? data, JsonNode? contextData = null)
{
var items = _items.Select(i => i.Apply(data, contextData));
Expand All @@ -27,4 +43,22 @@ public AndRule(Rule a, params Rule[] more)

return first;
}
}
}

internal class AndRuleJsonConverter : JsonConverter<AndRule>
{
public override AndRule? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var parameters = JsonSerializer.Deserialize<Rule[]>(ref reader, options);

if (parameters == null || parameters.Length == 0)
throw new JsonException("The and rule needs an array of parameters.");

return new AndRule(parameters[0], parameters.Skip(1).ToArray());
}

public override void Write(Utf8JsonWriter writer, AndRule value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
Loading

0 comments on commit b974805

Please sign in to comment.