perf: Use String Interpolation In Code Generation - #2278
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2278 +/- ##
===========================================
- Coverage 100.00% 99.98% -0.02%
===========================================
Files 191 191
Lines 9902 9927 +25
Branches 1904 1908 +4
===========================================
+ Hits 9902 9926 +24
- Partials 0 1 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Looks good, some tests that I would like to add and fix any issues, generated by AI so adjust as needed: using Refit.Generator;
namespace Refit.GeneratorTests;
/// <summary>Tests interpolated strings emitted by the generator.</summary>
public sealed class InterpolatedStringGenerationTests
{
/// <summary>Verifies literal interpolation braces are doubled.</summary>
/// <returns>A task representing the test.</returns>
[Test]
public async Task BuilderEscapesLiteralBraces()
{
var actual = new Emitter.InterpolatedStringBuilder()
.AppendLiteral("{zip}")
.AppendExpression("value")
.Build();
await Assert.That(actual).IsEqualTo("$\"{{zip}}{value}\"");
}
/// <summary>Verifies braces in a nested alias produce valid generated code.</summary>
/// <returns>A task representing the test.</returns>
[Test]
public Task NestedAliasContainingBracesCompiles() =>
AssertGeneratedCodeCompiles(
"""
using System.Threading.Tasks;
using Refit;
namespace RefitGeneratorTest;
public sealed class Inner
{
[AliasAs("{zip}")]
public string Zip { get; set; } = "";
}
public sealed class QueryModel
{
public Inner Nested { get; set; } = new();
}
public interface IGeneratedClient
{
[Get("/query")]
Task<string> Find([Query] QueryModel query);
}
""");
/// <summary>Verifies braces in a serializer name produce valid generated code.</summary>
/// <returns>A task representing the test.</returns>
[Test]
public Task NestedSerializerNameContainingBracesCompiles() =>
AssertGeneratedCodeCompiles(
"""
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Refit;
namespace RefitGeneratorTest;
public sealed class Inner
{
[JsonPropertyName("{zip}")]
public string Zip { get; set; } = "";
}
public sealed class QueryModel
{
public Inner Nested { get; set; } = new();
}
public interface IGeneratedClient
{
[Get("/query")]
Task<string> Find([Query] QueryModel query);
}
""");
/// <summary>Verifies braces in query prefixes and delimiters produce valid generated code.</summary>
/// <returns>A task representing the test.</returns>
[Test]
public Task QueryPrefixAndDelimiterContainingBracesCompile() =>
AssertGeneratedCodeCompiles(
"""
using System.Threading.Tasks;
using Refit;
namespace RefitGeneratorTest;
public sealed class Inner
{
public string Value { get; set; } = "";
}
public sealed class QueryModel
{
[Query("{delimiter}", "{prefix}")]
public Inner Nested { get; set; } = new();
}
public interface IGeneratedClient
{
[Get("/query")]
Task<string> Find([Query] QueryModel query);
}
""");
/// <summary>Verifies braces in authorization schemes produce valid generated code.</summary>
/// <returns>A task representing the test.</returns>
[Test]
public Task AuthorizationSchemeContainingBracesCompiles() =>
AssertGeneratedCodeCompiles(
"""
using System.Threading.Tasks;
using Refit;
namespace RefitGeneratorTest;
public interface IGeneratedClient
{
[Get("/query")]
Task<string> Find([Authorize("{Bearer}")] string token);
}
""");
/// <summary>Runs the generator and verifies its output compiles.</summary>
/// <param name="source">The source being compiled.</param>
/// <returns>A task representing the assertion.</returns>
private static async Task AssertGeneratedCodeCompiles(string source)
{
var result = Fixture.RunGenerator(source, generatedRequestBuilding: true);
await Assert.That(result.CompilationErrors).IsEmpty();
}
} |
I liked it what it created, however I tried to move some of the test to QueryRequestBuildingLiveTests.. To verify that the paths (Reflection vs code generation were working fine), but I couldn't fix the Reflection on NestedSerializerNameContainingBracesCompiles and I do not think that should be neeeded |
|
|
Thanks again. Just added another edge case to the tests. I'll do a release in a week or so on this one. |



What kind of change does this PR introduce?
Performace, it uses InterpolatedStrings instead of concatenating strings with a + on the code Generation. Initial change on #2277
What is the new behavior?
What is the current behavior?
What might this PR break?
Different scape sequences for interpolatedStrings? If it's so the code generated could not compile
Checklist
mainbranchAdditional information
I change the name of StringInterpolationHandler to StringInterpolationBuilder to be more accurate on the naming