diff --git a/docs/index.md b/docs/index.md index 1559107..63ff4d6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,7 @@ _layout: landing Primitively is a powerful C# source generator that transforms primitive identifiers and value objects into highly performant, customisable, read-only struct values that support ASP.NET model binding and validation (including FluentValidation), Open API standards, JSON and MongoDB BSON serialization, with zero or minimal configuration. -The forthcoming 1.4 release supports the following platforms: +The 1.4.x release supports the following platforms: - .NET 8 - .NET 7 @@ -17,6 +17,4 @@ The forthcoming 1.4 release supports the following platforms: - .NET Core 3.1¹ - .NET Standard 2.0¹ -Prior to the 1.4 release. Primitively supported .NET 6 only. - -¹ It is possible (but not recommended) to use Primitively on class libraries that target platforms that preceed .NET 6. Extra configuration is required for this to work because C# 10 is a minimum requirement. Please see the documentation for details. \ No newline at end of file +¹ It is possible (but not recommended) to use Primitively on class libraries that target platforms that preceed .NET 6. Extra configuration is required for this to work because C# 10 is a minimum requirement. To learn more, clone the Primitively git repo and open the example projects. \ No newline at end of file diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/Acme.Temperature.UnitTests.csproj b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/Acme.Temperature.UnitTests.csproj new file mode 100644 index 0000000..667f061 --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/Acme.Temperature.UnitTests.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + enable + enable + false + true + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/CelsiusTests.cs b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/CelsiusTests.cs new file mode 100644 index 0000000..070af5d --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/CelsiusTests.cs @@ -0,0 +1,76 @@ +namespace Acme.Temperature.UnitTests; + +public class CelsiusTests +{ + [Fact] + public void AbsoluteZero_In_Sut_Scale_Converts_To_AbsoluteZero_In_Other_Scales() + { + // Assign + var celsius = Celsius.AbsoluteZero; + + // Act + var fahrenheit = (Fahrenheit)celsius; + var kelvin = (Kelvin)celsius; + var rankine = (Rankine)celsius; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.AbsoluteZero); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.AbsoluteZero); + kelvin.Should().BeEquivalentTo(Kelvin.AbsoluteZero); + rankine.Should().BeEquivalentTo(Rankine.AbsoluteZero); + } + + [Fact] + public void WaterMeltingPoint_In_Sut_Scale_Converts_To_WaterMeltingPoint_In_Other_Scales() + { + // Assign + var celsius = Celsius.WaterMeltingPoint; + + // Act + var fahrenheit = (Fahrenheit)celsius; + var kelvin = (Kelvin)celsius; + var rankine = (Rankine)celsius; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.WaterMeltingPoint); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.WaterMeltingPoint); + kelvin.Should().BeEquivalentTo(Kelvin.WaterMeltingPoint); + rankine.Should().BeEquivalentTo(Rankine.WaterMeltingPoint); + } + + [Fact] + public void WaterBoilingPoint_In_Sut_Scale_Converts_To_WaterBoilingPoint_In_Other_Scales() + { + // Assign + var celsius = Celsius.WaterBoilingPoint; + + // Act + var fahrenheit = (Fahrenheit)celsius; + var kelvin = (Kelvin)celsius; + var rankine = (Rankine)celsius; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.WaterBoilingPoint); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.WaterBoilingPoint); + kelvin.Should().BeEquivalentTo(Kelvin.WaterBoilingPoint); + rankine.Should().BeEquivalentTo(Rankine.WaterBoilingPoint); + } + + [Fact] + public void Minimum_Sut_Scale_Converts_To_Minimum_In_Other_Scales() + { + // Assign + var celsius = new Celsius(Celsius.Minimum); + + // Act + double kelvin = (Kelvin)celsius; + double fahrenheit = (Fahrenheit)celsius; + double rankine = (Rankine)celsius; + + // Assert + celsius.Should().BeEquivalentTo((Celsius)Celsius.Minimum); + fahrenheit.Should().Be(Fahrenheit.Minimum); + kelvin.Should().Be(Kelvin.Minimum); + rankine.Should().Be(Rankine.Minimum); + } +} diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/FahrenheitTests.cs b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/FahrenheitTests.cs new file mode 100644 index 0000000..7285222 --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/FahrenheitTests.cs @@ -0,0 +1,76 @@ +namespace Acme.Temperature.UnitTests; + +public class FahrenheitTests +{ + [Fact] + public void AbsoluteZero_In_Sut_Scale_Converts_To_AbsoluteZero_In_Other_Scales() + { + // Assign + var fahrenheit = Fahrenheit.AbsoluteZero; + + // Act + var celsius = (Celsius)fahrenheit; + var kelvin = (Kelvin)fahrenheit; + var rankine = (Rankine)fahrenheit; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.AbsoluteZero); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.AbsoluteZero); + kelvin.Should().BeEquivalentTo(Kelvin.AbsoluteZero); + rankine.Should().BeEquivalentTo(Rankine.AbsoluteZero); + } + + [Fact] + public void WaterMeltingPoint_In_Sut_Scale_Converts_To_WaterMeltingPoint_In_Other_Scales() + { + // Assign + var fahrenheit = Fahrenheit.WaterMeltingPoint; + + // Act + var celsius = (Celsius)fahrenheit; + var kelvin = (Kelvin)fahrenheit; + var rankine = (Rankine)fahrenheit; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.WaterMeltingPoint); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.WaterMeltingPoint); + kelvin.Should().BeEquivalentTo(Kelvin.WaterMeltingPoint); + rankine.Should().BeEquivalentTo(Rankine.WaterMeltingPoint); + } + + [Fact] + public void WaterBoilingPoint_In_Sut_Scale_Converts_To_WaterBoilingPoint_In_Other_Scales() + { + // Assign + var fahrenheit = Fahrenheit.WaterBoilingPoint; + + // Act + var celsius = (Celsius)fahrenheit; + var kelvin = (Kelvin)fahrenheit; + var rankine = (Rankine)fahrenheit; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.WaterBoilingPoint); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.WaterBoilingPoint); + kelvin.Should().BeEquivalentTo(Kelvin.WaterBoilingPoint); + rankine.Should().BeEquivalentTo(Rankine.WaterBoilingPoint); + } + + [Fact] + public void Minimum_Sut_Scale_Converts_To_Minimum_In_Other_Scales() + { + // Assign + var fahrenheit = new Fahrenheit(Fahrenheit.Minimum); + + // Act + double celsius = (Celsius)fahrenheit; + double kelvin = (Kelvin)fahrenheit; + double rankine = (Rankine)fahrenheit; + + // Assert + celsius.Should().Be(Celsius.Minimum); + fahrenheit.Should().BeEquivalentTo((Fahrenheit)Fahrenheit.Minimum); + kelvin.Should().Be(Kelvin.Minimum); + rankine.Should().Be(Rankine.Minimum); + } +} diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/GlobalUsings.cs b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/GlobalUsings.cs new file mode 100644 index 0000000..30b9735 --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Xunit; +global using FluentAssertions; diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/KelvinTests.cs b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/KelvinTests.cs new file mode 100644 index 0000000..020f0ca --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/KelvinTests.cs @@ -0,0 +1,76 @@ +namespace Acme.Temperature.UnitTests; + +public class KelvinTests +{ + [Fact] + public void AbsoluteZero_In_Sut_Scale_Converts_To_AbsoluteZero_In_Other_Scales() + { + // Assign + var kelvin = Kelvin.AbsoluteZero; + + // Act + var celsius = (Celsius)kelvin; + var fahrenheit = (Fahrenheit)kelvin; + var rankine = (Rankine)kelvin; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.AbsoluteZero); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.AbsoluteZero); + kelvin.Should().BeEquivalentTo(Kelvin.AbsoluteZero); + rankine.Should().BeEquivalentTo(Rankine.AbsoluteZero); + } + + [Fact] + public void WaterMeltingPoint_In_Sut_Scale_Converts_To_WaterMeltingPoint_In_Other_Scales() + { + // Assign + var kelvin = Kelvin.WaterMeltingPoint; + + // Act + var celsius = (Celsius)kelvin; + var fahrenheit = (Fahrenheit)kelvin; + var rankine = (Rankine)kelvin; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.WaterMeltingPoint); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.WaterMeltingPoint); + kelvin.Should().BeEquivalentTo(Kelvin.WaterMeltingPoint); + rankine.Should().BeEquivalentTo(Rankine.WaterMeltingPoint); + } + + [Fact] + public void WaterBoilingPoint_In_Sut_Scale_Converts_To_WaterBoilingPoint_In_Other_Scales() + { + // Assign + var kelvin = Kelvin.WaterBoilingPoint; + + // Act + var celsius = (Celsius)kelvin; + var fahrenheit = (Fahrenheit)kelvin; + var rankine = (Rankine)kelvin; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.WaterBoilingPoint); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.WaterBoilingPoint); + kelvin.Should().BeEquivalentTo(Kelvin.WaterBoilingPoint); + rankine.Should().BeEquivalentTo(Rankine.WaterBoilingPoint); + } + + [Fact] + public void Minimum_Sut_Scale_Converts_To_Minimum_In_Other_Scales() + { + // Assign + var kelvin = new Kelvin(Kelvin.Minimum); + + // Act + double celsius = (Celsius)kelvin; + double fahrenheit = (Fahrenheit)kelvin; + double rankine = (Rankine)kelvin; + + // Assert + celsius.Should().Be(Celsius.Minimum); + fahrenheit.Should().Be(Fahrenheit.Minimum); + kelvin.Should().BeEquivalentTo((Kelvin)Kelvin.Minimum); + rankine.Should().Be(Rankine.Minimum); + } +} diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/RankineTests.cs b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/RankineTests.cs new file mode 100644 index 0000000..b4b8970 --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature.UnitTests/RankineTests.cs @@ -0,0 +1,76 @@ +namespace Acme.Temperature.UnitTests; + +public class RankineTests +{ + [Fact] + public void AbsoluteZero_In_Sut_Scale_Converts_To_AbsoluteZero_In_Other_Scales() + { + // Assign + var rankine = Rankine.AbsoluteZero; + + // Act + var celsius = (Celsius)rankine; + var fahrenheit = (Fahrenheit)rankine; + var kelvin = (Kelvin)rankine; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.AbsoluteZero); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.AbsoluteZero); + kelvin.Should().BeEquivalentTo(Kelvin.AbsoluteZero); + rankine.Should().BeEquivalentTo(Rankine.AbsoluteZero); + } + + [Fact] + public void WaterMeltingPoint_In_Sut_Scale_Converts_To_WaterMeltingPoint_In_Other_Scales() + { + // Assign + var rankine = Rankine.WaterMeltingPoint; + + // Act + var celsius = (Celsius)rankine; + var fahrenheit = (Fahrenheit)rankine; + var kelvin = (Kelvin)rankine; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.WaterMeltingPoint); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.WaterMeltingPoint); + kelvin.Should().BeEquivalentTo(Kelvin.WaterMeltingPoint); + rankine.Should().BeEquivalentTo(Rankine.WaterMeltingPoint); + } + + [Fact] + public void WaterBoilingPoint_In_Sut_Scale_Converts_To_WaterBoilingPoint_In_Other_Scales() + { + // Assign + var rankine = Rankine.WaterBoilingPoint; + + // Act + var celsius = (Celsius)rankine; + var fahrenheit = (Fahrenheit)rankine; + var kelvin = (Kelvin)rankine; + + // Assert + celsius.Should().BeEquivalentTo(Celsius.WaterBoilingPoint); + fahrenheit.Should().BeEquivalentTo(Fahrenheit.WaterBoilingPoint); + kelvin.Should().BeEquivalentTo(Kelvin.WaterBoilingPoint); + rankine.Should().BeEquivalentTo(Rankine.WaterBoilingPoint); + } + + [Fact] + public void Minimum_Sut_Scale_Converts_To_Minimum_In_Other_Scales() + { + // Assign + var rankine = new Rankine(Rankine.Minimum); + + // Act + double celsius = (Celsius)rankine; + double fahrenheit = (Fahrenheit)rankine; + double kelvin = (Kelvin)rankine; + + // Assert + celsius.Should().Be(Celsius.Minimum); + fahrenheit.Should().Be(Fahrenheit.Minimum); + kelvin.Should().Be(Kelvin.Minimum); + rankine.Should().BeEquivalentTo((Rankine)Rankine.Minimum); + } +} diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature/Acme.Temperature.csproj b/examples/03-temperature-units-of-measure/Acme.Temperature/Acme.Temperature.csproj new file mode 100644 index 0000000..67d8846 --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature/Acme.Temperature.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature/Acme.Temperature.sln b/examples/03-temperature-units-of-measure/Acme.Temperature/Acme.Temperature.sln new file mode 100644 index 0000000..1119bf4 --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature/Acme.Temperature.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34622.214 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.Temperature", "Acme.Temperature.csproj", "{CF73B1AF-CBD9-4786-B550-E6EC52DC2549}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Acme.Temperature.UnitTests", "..\Acme.Temperature.UnitTests\Acme.Temperature.UnitTests.csproj", "{CF7BE825-46CE-4B66-97E5-66D282DE7DCF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF73B1AF-CBD9-4786-B550-E6EC52DC2549}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF73B1AF-CBD9-4786-B550-E6EC52DC2549}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF73B1AF-CBD9-4786-B550-E6EC52DC2549}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF73B1AF-CBD9-4786-B550-E6EC52DC2549}.Release|Any CPU.Build.0 = Release|Any CPU + {CF7BE825-46CE-4B66-97E5-66D282DE7DCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF7BE825-46CE-4B66-97E5-66D282DE7DCF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF7BE825-46CE-4B66-97E5-66D282DE7DCF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF7BE825-46CE-4B66-97E5-66D282DE7DCF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6C5174C6-2AA7-44E7-AEAA-C98512786A87} + EndGlobalSection +EndGlobal diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature/Celsius.cs b/examples/03-temperature-units-of-measure/Acme.Temperature/Celsius.cs new file mode 100644 index 0000000..60529ea --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature/Celsius.cs @@ -0,0 +1,20 @@ +using Primitively; + +// https://en.wikipedia.org/wiki/Celsius +// https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature + +namespace Acme.Temperature; + +[Double(2, Minimum = -273.15d)] +public partial record struct Celsius : ITemperature +{ + public static Celsius AbsoluteZero => new(-273.15d); + + public static Celsius WaterMeltingPoint => new(0d); + + public static Celsius WaterBoilingPoint => new(99.9839d); + + public static explicit operator Kelvin(Celsius value) => new(value + 273.15d); + public static explicit operator Fahrenheit(Celsius value) => new((value * (9d / 5d)) + 32d); + public static explicit operator Rankine(Celsius value) => new((value + 273.15d) * (9d / 5d)); +} diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature/Fahrenheit.cs b/examples/03-temperature-units-of-measure/Acme.Temperature/Fahrenheit.cs new file mode 100644 index 0000000..4b1c7d2 --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature/Fahrenheit.cs @@ -0,0 +1,20 @@ +using Primitively; + +// https://en.wikipedia.org/wiki/Celsius +// https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature + +namespace Acme.Temperature; + +[Double(2, Minimum = -459.67d)] +public partial record struct Fahrenheit : ITemperature +{ + public static Fahrenheit AbsoluteZero => new(-459.67d); + + public static Fahrenheit WaterMeltingPoint => new(32d); + + public static Fahrenheit WaterBoilingPoint => new(211.97102d); + + public static explicit operator Celsius(Fahrenheit value) => new((value - 32d) * (5d / 9d)); + public static explicit operator Kelvin(Fahrenheit value) => new((value + 459.67d) * (5d / 9d)); + public static explicit operator Rankine(Fahrenheit value) => new(value + 459.67d); +} diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature/ITemperature.cs b/examples/03-temperature-units-of-measure/Acme.Temperature/ITemperature.cs new file mode 100644 index 0000000..904c15b --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature/ITemperature.cs @@ -0,0 +1,8 @@ +namespace Acme.Temperature; + +public interface ITemperature where T : ITemperature +{ + static abstract T AbsoluteZero { get; } + static abstract T WaterMeltingPoint { get; } + static abstract T WaterBoilingPoint { get; } +} diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature/Kelvin.cs b/examples/03-temperature-units-of-measure/Acme.Temperature/Kelvin.cs new file mode 100644 index 0000000..04d0fa3 --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature/Kelvin.cs @@ -0,0 +1,20 @@ +using Primitively; + +// https://en.wikipedia.org/wiki/Celsius +// https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature + +namespace Acme.Temperature; + +[Double(2, Minimum = 0d)] +public partial record struct Kelvin : ITemperature +{ + public static Kelvin AbsoluteZero => new(0d); + + public static Kelvin WaterMeltingPoint => new(273.15d); + + public static Kelvin WaterBoilingPoint => new(373.1339d); + + public static explicit operator Celsius(Kelvin value) => new(value - 273.15d); + public static explicit operator Fahrenheit(Kelvin value) => new((9d / 5d * value) - 459.67d); + public static explicit operator Rankine(Kelvin value) => new(9d / 5d * value); +} diff --git a/examples/03-temperature-units-of-measure/Acme.Temperature/Rankine.cs b/examples/03-temperature-units-of-measure/Acme.Temperature/Rankine.cs new file mode 100644 index 0000000..08c57fb --- /dev/null +++ b/examples/03-temperature-units-of-measure/Acme.Temperature/Rankine.cs @@ -0,0 +1,20 @@ +using Primitively; + +// https://en.wikipedia.org/wiki/Celsius +// https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature + +namespace Acme.Temperature; + +[Double(2, Minimum = 0d)] +public partial record struct Rankine : ITemperature +{ + public static Rankine AbsoluteZero => new(0d); + + public static Rankine WaterMeltingPoint => new(491.67d); + + public static Rankine WaterBoilingPoint => new(671.64102d); + + public static explicit operator Celsius(Rankine value) => new((5d / 9d * value) - 273.15d); + public static explicit operator Fahrenheit(Rankine value) => new(value - 459.67d); + public static explicit operator Kelvin(Rankine value) => new(5d / 9d * value); +} diff --git a/test/Acme.TestLib/Temperature.cs b/test/Acme.TestLib/Temperature.cs new file mode 100644 index 0000000..3710a40 --- /dev/null +++ b/test/Acme.TestLib/Temperature.cs @@ -0,0 +1,71 @@ +using Primitively; + +// https://en.wikipedia.org/wiki/Celsius +// https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature + +namespace Acme.TestLib; + +[Double(2, Minimum = -273.15d)] +public partial record struct Celsius : ITemperature +{ + public static Celsius AbsoluteZero => new(-273.15d); + + public static Celsius WaterMeltingPoint => new(0d); + + public static Celsius WaterBoilingPoint => new(99.9839d); + + public static explicit operator Kelvin(Celsius value) => new(value + 273.15d); + public static explicit operator Fahrenheit(Celsius value) => new((9d / 5d * value) + 32d); + public static explicit operator Rankine(Celsius value) => new((value + 273.15d) * (9d / 5d)); +} + +[Double(Minimum = -459.67d)] +public partial record struct Fahrenheit : ITemperature +{ + public static Fahrenheit AbsoluteZero => new(-459.67d); + + public static Fahrenheit WaterMeltingPoint => new(32d); + + public static Fahrenheit WaterBoilingPoint => new(211.97102d); + + public static explicit operator Celsius(Fahrenheit value) => new((value - 32d) * (5d / 9d)); + public static explicit operator Kelvin(Fahrenheit value) => new((value + 459.67d) * (5d / 9d)); + public static explicit operator Rankine(Fahrenheit value) => new(value + 459.67d); +} + +[Double(Minimum = 0d)] +public partial record struct Kelvin : ITemperature +{ + public static Kelvin AbsoluteZero => new(0d); + + public static Kelvin WaterMeltingPoint => new(273.15d); + + public static Kelvin WaterBoilingPoint => new(373.1339d); + + public static explicit operator Celsius(Kelvin value) => new(value - 273.15d); + public static explicit operator Fahrenheit(Kelvin value) => new((9d / 5d * value) - 459.67d); + public static explicit operator Rankine(Kelvin value) => new(9d / 5d * value); +} + +[Double(Minimum = 0d)] +public partial record struct Rankine : ITemperature +{ + public static Rankine AbsoluteZero => new(0d); + + public static Rankine WaterMeltingPoint => new(491.67d); + + public static Rankine WaterBoilingPoint => new(671.64102d); + + public static explicit operator Celsius(Rankine value) => new((5d / 9d * value) - 273.15d); + public static explicit operator Fahrenheit(Rankine value) => new(value - 459.67d); + public static explicit operator Kelvin(Rankine value) => new(5d / 9d * value); +} + +public interface ITemperature where T : ITemperature +{ +#if NET7_0_OR_GREATER + static abstract T AbsoluteZero { get; } + static abstract T WaterMeltingPoint { get; } + static abstract T WaterBoilingPoint { get; } +#endif +}