Skip to content

Commit

Permalink
Implement FluidValue.WriteToAsync
Browse files Browse the repository at this point in the history
Fixes #647
  • Loading branch information
sebastienros committed May 29, 2024
1 parent 1e9c992 commit 9588669
Show file tree
Hide file tree
Showing 34 changed files with 420 additions and 99 deletions.
2 changes: 1 addition & 1 deletion Fluid.MvcViewEngine/Fluid.MvcViewEngine.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<PackageIcon>logo_64x64.png</PackageIcon>
<IsPackable>true</IsPackable>
Expand Down
7 changes: 7 additions & 0 deletions Fluid.Tests/Domain/WithInterfaces/PetValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Globalization;
using System.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace Fluid.Tests.Domain.WithInterfaces
{
Expand Down Expand Up @@ -42,11 +43,17 @@ public override string ToStringValue()
throw new NotImplementedException();
}

[Obsolete("WriteTo is obsolete, prefer the WriteToAsync method.")]
public override void WriteTo(TextWriter writer, TextEncoder encoder, CultureInfo cultureInfo)
{
throw new NotImplementedException();
}

public override ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, CultureInfo cultureInfo)
{
throw new NotImplementedException();
}

protected override FluidValue GetValue(string name, TemplateContext context)
{
if (name == "Name")
Expand Down
30 changes: 30 additions & 0 deletions Fluid.Tests/MvcViewEngine/NoSyncStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace Fluid.Tests.MvcViewEngine
{
/// <summary>
/// Stream implementation that prevents non-async usages.
/// </summary>
public sealed class NoSyncStream : Stream
{
public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => Task.CompletedTask;
public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default) => ValueTask.CompletedTask;


public override void Flush() => throw new InvalidOperationException();
public override int Read(byte[] buffer, int offset, int count) => throw new InvalidOperationException();
public override long Seek(long offset, SeekOrigin origin) => throw new InvalidOperationException();
public override void SetLength(long value) => throw new InvalidOperationException();
public override void Write(byte[] buffer, int offset, int count) => throw new InvalidOperationException();

public override bool CanRead { get; } = false;
public override bool CanSeek { get; } = false;
public override bool CanWrite { get; } = true;
public override long Length { get; }
public override long Position { get; set; }
}
}
29 changes: 28 additions & 1 deletion Fluid.Tests/MvcViewEngine/ViewEngineTests.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using Fluid.Tests.Mocks;
using Fluid.ViewEngine;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Fluid.Tests.MvcViewEngine
{
public class ViewEngineTests
public partial class ViewEngineTests
{
FluidViewEngineOptions _options = new ();
FluidViewRenderer _renderer;
Expand Down Expand Up @@ -278,5 +281,29 @@ public async Task LayoutShouldBeAbleToIncludeVarsFromViewStart()

Assert.Equal("[TITLE][SUBTITLE][ViewStart][View]", sw.ToString());
}

[Fact]
public async Task RenderViewOnlyAsyncStream_LargePropertyValue_Nested_SmallBuffer_BiggerThan128LengthString()
{
_mockFileProvider.Add("Views/Index.liquid", "{% layout '_Layout' %}{% section bigboy %}{{BigString}}{% endsection %} ");
_mockFileProvider.Add("Views/_Layout.liquid", "{% rendersection bigboy %}");

await using var sw = new StreamWriter(new NoSyncStream(), bufferSize: 10);
var template = new TemplateContext(new { BigString = new string(Enumerable.Range(0, 129).Select(x => 'b').ToArray()) });
await _renderer.RenderViewAsync(sw, "Index.liquid", template);
await sw.FlushAsync();
}

[Fact]
public async Task RenderViewOnlyAsyncStream_LargePropertyValue_Nested()
{
_mockFileProvider.Add("Views/Index.liquid", "{% layout '_Layout' %}{% section bigboy %}{{BigString}}{% endsection %} ");
_mockFileProvider.Add("Views/_Layout.liquid", "{% rendersection bigboy %}");

await using var sw = new StreamWriter(new NoSyncStream());
var template = new TemplateContext(new { BigString = new string(Enumerable.Range(0, 1500).Select(_ => 'b').ToArray()) });
await _renderer.RenderViewAsync(sw, "Index.liquid", template);
await sw.FlushAsync();
}
}
}
2 changes: 1 addition & 1 deletion Fluid.ViewEngine/Fluid.ViewEngine.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<IsPackable>true</IsPackable>
<PackageIcon>logo_64x64.png</PackageIcon>
Expand Down
2 changes: 1 addition & 1 deletion Fluid/Accessors/PropertyInfoAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public PropertyInfoAccessor(PropertyInfo propertyInfo)
var d = propertyInfo.GetGetMethod().CreateDelegate(delegateType);

var invokerType = typeof(Invoker<,>).MakeGenericType(propertyInfo.DeclaringType, propertyInfo.PropertyType);
_invoker = Activator.CreateInstance(invokerType, [d]) as IInvoker;
_invoker = Activator.CreateInstance(invokerType, new object[] { d }) as IInvoker;
}

public object Get(object obj, string name, TemplateContext ctx)
Expand Down
2 changes: 1 addition & 1 deletion Fluid/Ast/CycleStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public override async ValueTask<Completion> WriteToAsync(TextWriter writer, Text
var value = await Values[(int)index].EvaluateAsync(context);
context.SetValue(groupValue, NumberValue.Create(index + 1));

value.WriteTo(writer, encoder, context.CultureInfo);
await value.WriteToAsync(writer, encoder, context.CultureInfo);

return Completion.Normal;
}
Expand Down
6 changes: 3 additions & 3 deletions Fluid/Ast/DecrementStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public DecrementStatement(string identifier)

public string Identifier { get; }

public override ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
public override async ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
{
context.IncrementSteps();

Expand All @@ -35,9 +35,9 @@ public override ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncode

context.SetValue(prefixedIdentifier, value);

value.WriteTo(writer, encoder, context.CultureInfo);
await value.WriteToAsync(writer, encoder, context.CultureInfo);

return Normal();
return Completion.Normal;
}

protected internal override Statement Accept(AstVisitor visitor) => visitor.VisitDecrementStatement(this);
Expand Down
15 changes: 13 additions & 2 deletions Fluid/Ast/IncrementStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,20 @@ public override ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncode

context.SetValue(prefixedIdentifier, value);

value.WriteTo(writer, encoder, context.CultureInfo);
var task = value.WriteToAsync(writer, encoder, context.CultureInfo);

return Normal();
if (task.IsCompletedSuccessfully)
{
return new ValueTask<Completion>(Completion.Normal);
}

return Awaited(task);

static async ValueTask<Completion> Awaited(ValueTask t)
{
await t;
return Completion.Normal;
}
}

protected internal override Statement Accept(AstVisitor visitor) => visitor.VisitIncrementStatement(this);
Expand Down
18 changes: 15 additions & 3 deletions Fluid/Ast/OutputStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ static async ValueTask<Completion> Awaited(
TemplateContext ctx)
{
var value = await t;
value.WriteTo(w, enc, ctx.CultureInfo);
await value.WriteToAsync(w, enc, ctx.CultureInfo);
return Completion.Normal;
}

Expand All @@ -32,8 +32,20 @@ static async ValueTask<Completion> Awaited(
var task = Expression.EvaluateAsync(context);
if (task.IsCompletedSuccessfully)
{
task.Result.WriteTo(writer, encoder, context.CultureInfo);
return new ValueTask<Completion>(Completion.Normal);
var valueTask = task.Result.WriteToAsync(writer, encoder, context.CultureInfo);

if (valueTask.IsCompletedSuccessfully)
{
return new ValueTask<Completion>(Completion.Normal);
}

return AwaitedWriteTo(valueTask);

static async ValueTask<Completion> AwaitedWriteTo(ValueTask t)
{
await t;
return Completion.Normal;
}
}

return Awaited(task, writer, encoder, context);
Expand Down
4 changes: 2 additions & 2 deletions Fluid/Filters/ColorFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ public static explicit operator HexColor(RgbColor rgbColor)
{
private const double DefaultTransperency = 1.0;

private static readonly char[] _colorSeparators = ['(', ',', ' ', ')'];
private static readonly char[] _colorSeparators = new[] { '(', ',', ' ', ')' };

public static readonly RgbColor Empty = default;

Expand Down Expand Up @@ -814,7 +814,7 @@ private readonly struct HslColor
{
private const double DefaultTransparency = 1.0;

private static readonly char[] _colorSeparators = ['(', ',', ' ', ')'];
private static readonly char[] _colorSeparators = new[] { '(', ',', ' ', ')' };

public static readonly HslColor Empty = default;

Expand Down
10 changes: 5 additions & 5 deletions Fluid/Filters/StringFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public static ValueTask<FluidValue> RemoveLast(FluidValue input, FilterArguments

public static ValueTask<FluidValue> ReplaceFirst(FluidValue input, FilterArguments arguments, TemplateContext context)
{
#if NET6_0_OR_GREATER
#if NETCOREAPP3_0_OR_GREATER
var value = input.ToStringValue().AsSpan();
var remove = arguments.At(0).ToStringValue().AsSpan();
#else
Expand All @@ -138,7 +138,7 @@ public static ValueTask<FluidValue> ReplaceFirst(FluidValue input, FilterArgumen
return input;
}

#if NET6_0_OR_GREATER
#if NETCOREAPP3_0_OR_GREATER
var concat = string.Concat(value.Slice(0, index), arguments.At(1).ToStringValue(), value.Slice(index + remove.Length));
#else
var concat = string.Concat(value.Substring(0, index), arguments.At(1).ToStringValue(), value.Substring(index + remove.Length));
Expand All @@ -153,7 +153,7 @@ public static ValueTask<FluidValue> Replace(FluidValue input, FilterArguments ar

public static ValueTask<FluidValue> ReplaceLast(FluidValue input, FilterArguments arguments, TemplateContext context)
{
#if NET6_0_OR_GREATER
#if NETCOREAPP3_0_OR_GREATER
var value = input.ToStringValue().AsSpan();
var remove = arguments.At(0).ToStringValue().AsSpan();
#else
Expand All @@ -167,7 +167,7 @@ public static ValueTask<FluidValue> ReplaceLast(FluidValue input, FilterArgument
return input;
}

#if NET6_0_OR_GREATER
#if NETCOREAPP3_0_OR_GREATER
var concat = string.Concat(value.Slice(0, index), arguments.At(1).ToStringValue(), value.Slice(index + remove.Length));
#else
var concat = string.Concat(value.Substring(0, index), arguments.At(1).ToStringValue(), value.Substring(index + remove.Length));
Expand Down Expand Up @@ -316,7 +316,7 @@ public static ValueTask<FluidValue> Truncate(FluidValue input, FilterArguments a

var l = Math.Max(0, length - ellipsisStr.Length);

#if NET6_0_OR_GREATER
#if NETCOREAPP3_0_OR_GREATER
var concat = string.Concat(inputStr.AsSpan().Slice(0, l), ellipsisStr);
#else
var concat = string.Concat(inputStr.Substring(0, l), ellipsisStr);
Expand Down
4 changes: 4 additions & 0 deletions Fluid/FiltersCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ public FilterCollection(int capacity = 0)

public int Count => _filters == null ? 0 : _filters.Count;

#if NETSTANDARD2_1
public void EnsureCapacity(int capacity) => _filters.EnsureCapacity(capacity);
#endif

public void AddFilter(string name, FilterDelegate d)
{
_filters ??= new Dictionary<string, FilterDelegate>();
Expand Down
10 changes: 9 additions & 1 deletion Fluid/Fluid.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<IsPackable>true</IsPackable>
Expand Down Expand Up @@ -36,10 +36,18 @@
<PackageReference Include="Microsoft.CSharp" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.1'">
<PackageReference Include="System.Text.Json" />
<PackageReference Include="Microsoft.CSharp" />
</ItemGroup>

<!-- Keep specific targets since it removes some dependencies -->
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='net7.0'">
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
</ItemGroup>

Expand Down
16 changes: 15 additions & 1 deletion Fluid/FluidParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,21 @@ public FluidParser(FluidParserOptions parserOptions)
Dot.SkipAnd(Identifier.Then<MemberSegment>(x => new IdentifierSegment(x)))
.Or(Indexer)
.Or(Call)))
.Then(x => new MemberExpression([x.Item1, .. x.Item2]));
.Then(x =>
{
if (x.Item2.Count == 0)
{
return new MemberExpression(x.Item1);
}

var list = new List<MemberSegment>(x.Item2.Count + 1);
list.Add(x.Item1);
list.AddRange(x.Item2);

return new MemberExpression(list);

// return new MemberExpression([x.Item1, .. x.Item2]);
});

var Range = LParen
.SkipAnd(OneOf(Integer, Member.Then<Expression>(x => x)))
Expand Down
Loading

0 comments on commit 9588669

Please sign in to comment.