diff --git a/Fluid.Tests/ArrayFiltersTests.cs b/Fluid.Tests/ArrayFiltersTests.cs index 2e31f6a3..da7084b0 100644 --- a/Fluid.Tests/ArrayFiltersTests.cs +++ b/Fluid.Tests/ArrayFiltersTests.cs @@ -659,5 +659,195 @@ public async Task SumWithDecimalsAndArguments(string filterArgument, decimal exp Assert.Equal(expectedValue, result.ToNumberValue()); } + + [Fact] + public async Task Reject() + { + var input = new ArrayValue(new[] { + new ObjectValue(new { Title = "a", Pinned = true }), + new ObjectValue(new { Title = "b", Pinned = false }), + new ObjectValue(new { Title = "c", Pinned = true }) + }); + + var options = new TemplateOptions(); + var context = new TemplateContext(options); + options.MemberAccessStrategy.Register(new { Title = "a", Pinned = true }.GetType()); + + var arguments1 = new FilterArguments().Add(new StringValue("Pinned")); + + var result1 = await ArrayFilters.Reject(input, arguments1, context); + + Assert.Single(result1.Enumerate(context)); + + var arguments2 = new FilterArguments() + .Add(new StringValue("Pinned")) + .Add(BooleanValue.Create(false)) + ; + + var result2 = await ArrayFilters.Reject(input, arguments2, context); + + Assert.Equal(2, result2.Enumerate(context).Count()); + + var arguments3 = new FilterArguments() + .Add(new StringValue("Title")) + .Add(new StringValue("c")); + + var result3 = await ArrayFilters.Reject(input, arguments3, context); + + Assert.Equal(2, result3.Enumerate(context).Count()); + } + + [Fact] + public async Task Find() + { + var input = new ArrayValue(new[] { + new ObjectValue(new { Title = "a", Pinned = true }), + new ObjectValue(new { Title = "b", Pinned = false }), + new ObjectValue(new { Title = "c", Pinned = true }) + }); + + var options = new TemplateOptions(); + var context = new TemplateContext(options); + options.MemberAccessStrategy.Register(new { Title = "a", Pinned = true }.GetType()); + + var arguments1 = new FilterArguments().Add(new StringValue("Pinned")).Add(BooleanValue.True); + + var result1 = await ArrayFilters.Find(input, arguments1, context); + + Assert.Equal(input.Values[0], result1); + + var arguments2 = new FilterArguments() + .Add(new StringValue("Pinned")) + .Add(BooleanValue.Create(false)) + ; + + var result2 = await ArrayFilters.Find(input, arguments2, context); + + Assert.Equal(input.Values[1], result2); + + var arguments3 = new FilterArguments() + .Add(new StringValue("Title")) + .Add(new StringValue("c")); + + var result3 = await ArrayFilters.Find(input, arguments3, context); + + Assert.Equal(input.Values[2], result3); + + var arguments4 = new FilterArguments(); + + var result4 = await ArrayFilters.Find(input, arguments4, context); + + Assert.Equal(NilValue.Instance, result4); + + var arguments5 = new FilterArguments() + .Add(new StringValue("Title")) + .Add(new StringValue("d")); + + var result5 = await ArrayFilters.Find(input, arguments5, context); + + Assert.Equal(NilValue.Instance, result5); + } + + [Fact] + public async Task FindIndex() + { + var input = new ArrayValue(new[] { + new ObjectValue(new { Title = "a", Pinned = true }), + new ObjectValue(new { Title = "b", Pinned = false }), + new ObjectValue(new { Title = "c", Pinned = true }) + }); + + var options = new TemplateOptions(); + var context = new TemplateContext(options); + options.MemberAccessStrategy.Register(new { Title = "a", Pinned = true }.GetType()); + + var arguments1 = new FilterArguments().Add(new StringValue("Pinned")).Add(BooleanValue.True); + + var result1 = await ArrayFilters.FindIndex(input, arguments1, context); + + Assert.Equal(0, result1.ToNumberValue()); + + var arguments2 = new FilterArguments() + .Add(new StringValue("Pinned")) + .Add(BooleanValue.Create(false)) + ; + + var result2 = await ArrayFilters.FindIndex(input, arguments2, context); + + Assert.Equal(1, result2.ToNumberValue()); + + var arguments3 = new FilterArguments() + .Add(new StringValue("Title")) + .Add(new StringValue("c")); + + var result3 = await ArrayFilters.FindIndex(input, arguments3, context); + + Assert.Equal(2, result3.ToNumberValue()); + + var arguments4 = new FilterArguments(); + + var result4 = await ArrayFilters.FindIndex(input, arguments4, context); + + Assert.Equal(NilValue.Instance, result4); + + var arguments5 = new FilterArguments() + .Add(new StringValue("Title")) + .Add(new StringValue("d")); + + var result5 = await ArrayFilters.FindIndex(input, arguments5, context); + + Assert.Equal(NilValue.Instance, result5); + } + + [Fact] + public async Task Has() + { + var input = new ArrayValue(new[] { + new ObjectValue(new { Title = "a", Pinned = true }), + new ObjectValue(new { Title = "b", Pinned = false }), + new ObjectValue(new { Title = "c", Pinned = true }) + }); + + var options = new TemplateOptions(); + var context = new TemplateContext(options); + options.MemberAccessStrategy.Register(new { Title = "a", Pinned = true }.GetType()); + + var arguments1 = new FilterArguments().Add(new StringValue("Pinned")).Add(BooleanValue.True); + + var result1 = await ArrayFilters.Has(input, arguments1, context); + + Assert.Equal(BooleanValue.True, result1); + + var arguments2 = new FilterArguments() + .Add(new StringValue("Pinned")) + .Add(BooleanValue.Create(false)) + ; + + var result2 = await ArrayFilters.Has(input, arguments2, context); + + Assert.Equal(BooleanValue.True, result2); + + var arguments3 = new FilterArguments() + .Add(new StringValue("Title")) + .Add(new StringValue("c")); + + var result3 = await ArrayFilters.Has(input, arguments3, context); + + Assert.Equal(BooleanValue.True, result3); + + var arguments4 = new FilterArguments(); + + var result4 = await ArrayFilters.Has(input, arguments4, context); + + Assert.Equal(BooleanValue.False, result4); + + var arguments5 = new FilterArguments() + .Add(new StringValue("Title")) + .Add(new StringValue("d")); + + var result5 = await ArrayFilters.Has(input, arguments5, context); + + Assert.Equal(BooleanValue.False, result5); + } } } diff --git a/Fluid/Filters/ArrayFilters.cs b/Fluid/Filters/ArrayFilters.cs index 2af0020b..bfe2a639 100644 --- a/Fluid/Filters/ArrayFilters.cs +++ b/Fluid/Filters/ArrayFilters.cs @@ -1,4 +1,4 @@ -using Fluid.Values; +using Fluid.Values; namespace Fluid.Filters { @@ -18,6 +18,10 @@ public static FilterCollection WithArrayFilters(this FilterCollection filters) filters.AddFilter("uniq", Uniq); filters.AddFilter("where", Where); filters.AddFilter("sum", Sum); + filters.AddFilter("find", Find); + filters.AddFilter("find_index", FindIndex); + filters.AddFilter("has", Has); + filters.AddFilter("reject", Reject); return filters; } @@ -162,6 +166,114 @@ public static async ValueTask Where(FluidValue input, FilterArgument return new ArrayValue(list); } + public static async ValueTask Find(FluidValue input, FilterArguments arguments, TemplateContext context) + { + if (input.Type != FluidValues.Array) + { + return input; + } + + // First argument is the property name to match + var member = arguments.At(0).ToStringValue(); + + // Second argument is the value to match + var targetValue = arguments.At(1); + if (targetValue.IsNil()) + { + return NilValue.Instance; + } + + FluidValue result = NilValue.Instance; + + foreach (var item in input.Enumerate(context)) + { + var itemValue = await item.GetValueAsync(member, context); + + if (targetValue.Equals(itemValue)) + { + result = item; + break; + } + } + + return result; + } + + public static async ValueTask FindIndex(FluidValue input, FilterArguments arguments, TemplateContext context) + { + if (input.Type != FluidValues.Array) + { + return input; + } + + // First argument is the property name to match + var member = arguments.At(0).ToStringValue(); + + // Second argument is the value to match + var targetValue = arguments.At(1); + if (targetValue.IsNil()) + { + return NilValue.Instance; + } + + FluidValue result = NilValue.Instance; + var index = 0; + + foreach (var item in input.Enumerate(context)) + { + var itemValue = await item.GetValueAsync(member, context); + + if (targetValue.Equals(itemValue)) + { + result = NumberValue.Create(index); + break; + } + + index++; + } + + return result; + } + + public static async ValueTask Has(FluidValue input, FilterArguments arguments, TemplateContext context) + { + var result = await Find(input, arguments, context); + + return result.Equals(NilValue.Instance) ? BooleanValue.False : BooleanValue.True; + } + + public static async ValueTask Reject(FluidValue input, FilterArguments arguments, TemplateContext context) + { + if (input.Type != FluidValues.Array) + { + return input; + } + + // First argument is the property name to match + var member = arguments.At(0).ToStringValue(); + + // Second argument is the value to not match, or 'true' if none is defined + var targetValue = arguments.At(1); + if (targetValue.IsNil()) + { + targetValue = BooleanValue.True; + } + + var list = new List(); + + foreach (var item in input.Enumerate(context)) + { + var itemValue = await item.GetValueAsync(member, context); + + if (!targetValue.Equals(itemValue)) + { + list.Add(item); + } + } + + return new ArrayValue(list); + } + public static ValueTask Size(FluidValue input, FilterArguments arguments, TemplateContext context) { return input.GetValueAsync("size", context);