Skip to content

Commit bce623e

Browse files
authored
Describe UnitsNet.Modular numeric format strings (#1716)
## Motivation UnitsNet.Modular forwards quantity format strings such as `F1` to the stored numeric value, but its public APIs do not currently identify those parameters as numeric format strings to IDE tooling. ## Changes - annotate generated quantity `ToString(string)` overloads with `StringSyntaxAttribute.NumericFormat` - annotate the type-erased `IQuantityDescriptor.Format` format parameter - verify the annotations are present in emitted consumer-visible metadata This improves validation and completion for direct format-string arguments where supported by the IDE. Interpolation clauses such as `$"{speed:F1}"` continue to work, but their completion behavior remains controlled by the IDE's interpolated-string support. ## Validation - `dotnet test UnitsNet.Modular/UnitsNet.Modular.slnx --no-restore -m:1 -p:UnitsNetModularSampleUpdateLocalPackagesOnBuild=false` - 115 tests passed - Modular runtime built for .NET 8, .NET 9, and .NET 10
1 parent 5aad11f commit bce623e

3 files changed

Lines changed: 31 additions & 2 deletions

File tree

‎UnitsNet.Modular/UnitsNet.Modular.Generator/QuantityEmitter.cs‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ namespace UnitsNet.Modular.Generator;
1010

1111
internal static class QuantityEmitter
1212
{
13+
private const string NumericFormatStringSyntaxAttribute =
14+
"global::System.Diagnostics.CodeAnalysis.StringSyntax(" +
15+
"global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)";
16+
1317
public static string Emit(
1418
QuantitySelection selection,
1519
IReadOnlyList<EmittedQuantityRelation> relationships,
@@ -222,8 +226,13 @@ public static string Emit(
222226
writer.AppendLine(" public override int GetHashCode() => BaseValue.GetHashCode();");
223227
writer.AppendLine(" public override string ToString() => ToString(null, null);");
224228
writer.AppendLine(" public string ToString(global::System.IFormatProvider? formatProvider) => ToString(null, formatProvider);");
225-
writer.AppendLine(" public string ToString(string? format) => ToString(format, null);");
226-
writer.AppendLine(" public string ToString(string? format, global::System.IFormatProvider? formatProvider) =>");
229+
writer.AppendLine(" public string ToString(");
230+
writer.Append(" [").Append(NumericFormatStringSyntaxAttribute).AppendLine("]");
231+
writer.AppendLine(" string? format) => ToString(format, null);");
232+
writer.AppendLine(" public string ToString(");
233+
writer.Append(" [").Append(NumericFormatStringSyntaxAttribute).AppendLine("]");
234+
writer.AppendLine(" string? format,");
235+
writer.AppendLine(" global::System.IFormatProvider? formatProvider) =>");
227236
writer.AppendLine(" global::UnitsNet.Modular.SourceGen.QuantityOperations.Format(_value, Unit, format, formatProvider, Metadata);");
228237
writer.AppendLine();
229238
if (quantity.IsLogarithmic)

‎UnitsNet.Modular/UnitsNet.Modular.Tests/GeneratedQuantityTests.cs‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Licensed under MIT No Attribution, see LICENSE file at the root.
22

3+
using System.Diagnostics.CodeAnalysis;
4+
using System.Reflection;
35
using System.Text.Json;
46
using Fictional.Measurements;
57
using UnitsNet;
@@ -10,6 +12,23 @@ namespace UnitsNet.Modular.Tests;
1012

1113
public sealed class GeneratedQuantityTests
1214
{
15+
[Fact]
16+
public void FormattingApis_IdentifyNumericFormatSyntax()
17+
{
18+
MethodInfo quantityToString = typeof(Length).GetMethod(nameof(Length.ToString), [typeof(string)])!;
19+
StringSyntaxAttribute? quantitySyntax = quantityToString
20+
.GetParameters()[0]
21+
.GetCustomAttribute<StringSyntaxAttribute>();
22+
23+
MethodInfo descriptorFormat = typeof(IQuantityDescriptor).GetMethod(nameof(IQuantityDescriptor.Format))!;
24+
StringSyntaxAttribute? descriptorSyntax = descriptorFormat
25+
.GetParameters()[1]
26+
.GetCustomAttribute<StringSyntaxAttribute>();
27+
28+
Assert.Equal(StringSyntaxAttribute.NumericFormat, quantitySyntax?.Syntax);
29+
Assert.Equal(StringSyntaxAttribute.NumericFormat, descriptorSyntax?.Syntax);
30+
}
31+
1332
[Fact]
1433
public void GeneratedRegistry_SystemTextJsonRoundTripsBuiltInAndCustomQuantities()
1534
{

‎UnitsNet.Modular/UnitsNet.Modular/Metadata/QuantityDescriptor.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ bool TryParse(
8989
/// <summary>Formats a generated quantity after validating its concrete type.</summary>
9090
string Format(
9191
IQuantity<double> quantity,
92+
[System.Diagnostics.CodeAnalysis.StringSyntax(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
9293
string? format = null,
9394
IFormatProvider? formatProvider = null);
9495
}

0 commit comments

Comments
 (0)