Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Build/build-functions.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ function Start-Tests {
$projectPaths = @(
"UnitsNet.Tests\UnitsNet.Tests.csproj",
"UnitsNet.NumberExtensions.Tests\UnitsNet.NumberExtensions.Tests.csproj",
"UnitsNet.NumberExtensions.CS14.Tests\UnitsNet.NumberExtensions.CS14.Tests.csproj",
"UnitsNet.Serialization.JsonNet.Tests\UnitsNet.Serialization.JsonNet.Tests.csproj"
)

Expand Down Expand Up @@ -129,7 +130,8 @@ function Start-PackNugets {
$projectPaths = @(
"UnitsNet\UnitsNet.csproj",
"UnitsNet.Serialization.JsonNet\UnitsNet.Serialization.JsonNet.csproj",
"UnitsNet.NumberExtensions\UnitsNet.NumberExtensions.csproj"
"UnitsNet.NumberExtensions\UnitsNet.NumberExtensions.csproj",
"UnitsNet.NumberExtensions.CS14\UnitsNet.NumberExtensions.CS14.csproj"
)

write-host -foreground blue "Pack nugets (dotnet CLI)...`n---"
Expand Down
82 changes: 82 additions & 0 deletions CodeGen/Generators/UnitsNetGen/NumberExtensionsCS14Generator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using CodeGen.JsonTypes;

namespace CodeGen.Generators.UnitsNetGen
{
internal class NumberExtensionsCS14Generator(Quantity quantity) : GeneratorBase
{
private readonly Quantity _quantity = quantity ?? throw new ArgumentNullException(nameof(quantity));
private readonly Unit[] _units = quantity.Units;
private readonly string _quantityName = quantity.Name;

public string Generate()
{
Writer.WL(GeneratedFileHeader);

Writer.WL(
$@"
using System;

#if NET7_0_OR_GREATER
using System.Numerics;
#endif

#nullable enable

namespace UnitsNet.NumberExtensions.NumberTo{_quantityName}
{{
/// <summary>
/// A number to {_quantityName} Extensions
/// </summary>");

Writer.WLIfText(1, GetObsoleteAttributeOrNull(_quantity));
Writer.WL(@$"
public static class NumberTo{_quantityName}Extensions
{{");

Writer.WL(@"
#pragma warning disable CS1591
extension<T>(T value)
#pragma warning restore CS1591
where T : notnull
#if NET7_0_OR_GREATER
, INumber<T>
#else
, IConvertible
#endif
{");


foreach (var unit in _units)
{
if (unit.SkipConversionGeneration)
continue;

Writer.WL(3, $@"
/// <inheritdoc cref=""{_quantityName}.From{unit.PluralName}(double)"" />");

// Include obsolete text from the quantity per extension method, to make it visible when the class is not explicitly referenced in code.
Writer.WLIfText(3, GetObsoleteAttributeOrNull(unit.ObsoleteText ?? _quantity.ObsoleteText));

Writer.WL(3, $@"public {_quantityName} {unit.PluralName}
#if NET7_0_OR_GREATER
=> {_quantityName}.From{unit.PluralName}(double.CreateChecked(value));
#else
=> {_quantityName}.From{unit.PluralName}(value.ToDouble(null));
#endif
");
}

Writer.WL(2, @"}
}
}");
return Writer.ToString();
}

/// <inheritdoc cref="GetObsoleteAttributeOrNull(string)"/>
private static string? GetObsoleteAttributeOrNull(Quantity quantity) => GetObsoleteAttributeOrNull(quantity.ObsoleteText);

private static string? GetObsoleteAttributeOrNull(string? obsoleteText) =>
string.IsNullOrWhiteSpace(obsoleteText) ? null : $"[Obsolete(\"{obsoleteText}\")]";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using CodeGen.JsonTypes;

namespace CodeGen.Generators.UnitsNetGen
{
internal class NumberExtensionsCS14TestClassGenerator : GeneratorBase
{
private readonly Unit[] _units;
private readonly string _quantityName;

public NumberExtensionsCS14TestClassGenerator(Quantity quantity)
{
if (quantity is null) throw new ArgumentNullException(nameof(quantity));

_units = quantity.Units;
_quantityName = quantity.Name;
}

public string Generate()
{
Writer.WL(GeneratedFileHeader);

Writer.WL(
$@"
using UnitsNet.NumberExtensions.NumberTo{_quantityName};
using Xunit;

namespace UnitsNet.Tests
{{
public class NumberTo{_quantityName}ExtensionsTests
{{");

foreach (var unit in _units)
{
if (unit.SkipConversionGeneration)
continue;

Writer.WL(2, $@"
[Fact]");

Writer.WL(2, $@"public void NumberTo{unit.PluralName}Test() =>
Assert.Equal({_quantityName}.From{unit.PluralName}(2), 2.{unit.PluralName});
");
}

Writer.WL(1, @"}
}");
return Writer.ToString();
}
}
}
18 changes: 18 additions & 0 deletions CodeGen/Generators/UnitsNetGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,17 @@ public static void Generate(string rootDir, Quantity[] quantities, QuantityNameT
var outputDir = $"{rootDir}/UnitsNet/GeneratedCode";
var extensionsOutputDir = $"{rootDir}/UnitsNet.NumberExtensions/GeneratedCode";
var extensionsTestOutputDir = $"{rootDir}/UnitsNet.NumberExtensions.Tests/GeneratedCode";
var extensionsCs14OutputDir = $"{rootDir}/UnitsNet.NumberExtensions.CS14/GeneratedCode";
var extensionsCs14TestOutputDir = $"{rootDir}/UnitsNet.NumberExtensions.CS14.Tests/GeneratedCode";
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CS14 projects are NOT included in the build-functions.psm1 file. This means:

Build Script Issues Found

The new CS14 projects are missing from the build pipeline:

  1. Not in test list (line 63-67): UnitsNet.NumberExtensions.CS14.Tests is not included in the Start-Tests function
  2. Not in NuGet pack list (line 102-106): UnitsNet.NumberExtensions.CS14 is not included in the Start-PackNugets function

This means:

  • ✅ The projects ARE in the solution file and will build with dotnet build
  • ❌ The tests won't run in CI/CD pipeline
  • ❌ The NuGet package won't be created in CI/CD pipeline

Here's the additional feedback to add to the PR review:

🔴 Build Pipeline Issue

The new CS14 projects are missing from the build pipeline configuration:

File: Build/build-functions.psm1

  1. Tests not running in CI - Add to line ~65:
$projectPaths = @(
    "UnitsNet.Tests\UnitsNet.Tests.csproj",
    "UnitsNet.NumberExtensions.Tests\UnitsNet.NumberExtensions.Tests.csproj",
    "UnitsNet.NumberExtensions.CS14.Tests\UnitsNet.NumberExtensions.CS14.Tests.csproj",  # ADD THIS
    "UnitsNet.Serialization.JsonNet.Tests\UnitsNet.Serialization.JsonNet.Tests.csproj"
)

2. NuGet package not created in CI - Add to line ~105:
$projectPaths = @(
    "UnitsNet\UnitsNet.csproj",
    "UnitsNet.Serialization.JsonNet\UnitsNet.Serialization.JsonNet.csproj",
    "UnitsNet.NumberExtensions\UnitsNet.NumberExtensions.csproj",
    "UnitsNet.NumberExtensions.CS14\UnitsNet.NumberExtensions.CS14.csproj"  # ADD THIS
)

Without these changes, the CS14 package won't be tested or published by the CI/CD pipeline.

This is a critical issue that would prevent the package from being properly tested and published.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could merge this earlier if we get the CI pipeline to build and run tests with .NET 10 SDK preview.

I pushed an attempt to fix this, let's see if the pipeline works now.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude happily fixed these issues, so I pushed a fix for the build scripts also.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, thanks. I didn't check the Package Id, description and the build script. Sorry about that.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, that's what reviews are for 👍

var testProjectDir = $"{rootDir}/UnitsNet.Tests";

// Ensure output directories exist
Directory.CreateDirectory($"{outputDir}/Quantities");
Directory.CreateDirectory($"{outputDir}/Units");
Directory.CreateDirectory($"{extensionsOutputDir}");
Directory.CreateDirectory($"{extensionsTestOutputDir}");
Directory.CreateDirectory($"{extensionsCs14OutputDir}");
Directory.CreateDirectory($"{extensionsCs14TestOutputDir}");
Directory.CreateDirectory($"{testProjectDir}/GeneratedCode");
Directory.CreateDirectory($"{testProjectDir}/GeneratedCode/TestsBase");
Directory.CreateDirectory($"{testProjectDir}/GeneratedCode/QuantityTests");
Expand All @@ -58,6 +62,8 @@ public static void Generate(string rootDir, Quantity[] quantities, QuantityNameT
GenerateUnitType(quantity, $"{outputDir}/Units/{quantity.Name}Unit.g.cs", unitEnumValues);
GenerateNumberToExtensions(quantity, $"{extensionsOutputDir}/NumberTo{quantity.Name}Extensions.g.cs");
GenerateNumberToExtensionsTestClass(quantity, $"{extensionsTestOutputDir}/NumberTo{quantity.Name}ExtensionsTest.g.cs");
GenerateNumberToExtensionsCS14(quantity, $"{extensionsCs14OutputDir}/NumberTo{quantity.Name}Extensions.g.cs");
GenerateNumberToExtensionsCS14TestClass(quantity, $"{extensionsCs14TestOutputDir}/NumberTo{quantity.Name}ExtensionsTest.g.cs");

// Example: CustomCode/Quantities/LengthTests inherits GeneratedCode/TestsBase/LengthTestsBase
// This way when new units are added to the quantity JSON definition, we auto-generate the new
Expand Down Expand Up @@ -107,6 +113,18 @@ private static void GenerateNumberToExtensionsTestClass(Quantity quantity, strin
File.WriteAllText(filePath, content);
}

private static void GenerateNumberToExtensionsCS14(Quantity quantity, string filePath)
{
var content = new NumberExtensionsCS14Generator(quantity).Generate();
File.WriteAllText(filePath, content);
}

private static void GenerateNumberToExtensionsCS14TestClass(Quantity quantity, string filePath)
{
var content = new NumberExtensionsCS14TestClassGenerator(quantity).Generate();
File.WriteAllText(filePath, content);
}

private static void GenerateUnitType(Quantity quantity, string filePath, UnitEnumNameToValue unitEnumValues)
{
var content = new UnitTypeGenerator(quantity, unitEnumValues).Generate();
Expand Down
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,49 @@ dotnet add package UnitsNet

or go to [NuGet Gallery | UnitsNet](https://www.nuget.org/packages/UnitsNet) for detailed instructions.

#### Build Targets
### Build Targets

* .NET Standard 2.0
* .NET 8.0 (LTS)
* .NET 9.0 (latest stable)
* .NET 10.0 (preview)
* [.NET nanoFramework](https://www.nanoframework.net/)


### Extension Packages

#### UnitsNet.NumberExtensions.CS14 (C# 14 Extension Members)

For C# 14 projects, use the new extension members syntax (no parentheses):

```bash
dotnet add package UnitsNet.NumberExtensions.CS14
```

```C#
using UnitsNet.NumberExtensions.NumberToLength;

// C# 14 extension members syntax, without parantheses
Length distance = 5.Meters; // Instead of Length.FromMeters(5)
```

> **Note:** Requires '<LangVersion>preview</LangVersion>' and .NET 10 SDK preview for now.

#### UnitsNet.NumberExtensions (Classic)

For C# 13 and lower (.NET SDK 9 or lower), use the classic extension methods:

```bash
dotnet add package UnitsNet.NumberExtensions
```

```C#
using UnitsNet.NumberExtensions.NumberToLength;

// Classic extension methods, with parentheses
Length distance = 5.Meters(); // Instead of Length.FromMeters(5)
```

### Static Typing

```C#
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading