Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<Version>2025.11.20.2</Version>
<Version>2025.11.20.3</Version>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
Expand Down
1 change: 1 addition & 0 deletions Tests/TedToolkit.Console/Absorptance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ public static void Test()
double d = a;
AbsorbedDose e = 10.0;
}

}
2 changes: 1 addition & 1 deletion Tests/TedToolkit.Console/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
[assembly: NumberExtension<int, Angle>]
[assembly: NumberExtension<double, Angle>]

[assembly:Quantities<double>(QuantitySystems.SI)]
[assembly:Quantities<double>(QuantitySystems.SI, "AbsorbedDose")]
9 changes: 8 additions & 1 deletion Tests/TedToolkit.Console/TestValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,15 @@ public partial class DateTimeResult
}

[GenerateValidResult<TestClass2>]
public partial class TestClass2Result
public partial class TestClass2Result;


[GenerateValidResult<TestRecord>]
public partial class TestRecordResult;

public record TestRecord
{
public int Value { get; init; } = 0;
}

public struct TestStruct;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,25 @@ public void GenerateCode(SourceProductionContext context)
])))))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),

MethodDeclaration(IdentifierName(quantity.Name), Identifier("Clamp"))
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)))
.WithAttributeLists([GeneratedCodeAttribute(typeof(QuantityStructGenerator))])
.WithXmlCommentInheritDoc(
$"global::System.Math.Clamp({typeName.FullName}, {typeName.FullName}, {typeName.FullName})")
.WithParameterList(ParameterList(
[
Parameter(Identifier("min")).WithType(IdentifierName(quantity.Name)),
Parameter(Identifier("max")).WithType(IdentifierName(quantity.Name))
]))
.WithExpressionBody(ArrowExpressionClause(InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
InvocationExpression(IdentifierName("Max"))
.WithArgumentList(ArgumentList(
[Argument(IdentifierName("min"))])),
IdentifierName("Min")))
.WithArgumentList(ArgumentList([Argument(IdentifierName("max"))]))))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),

PropertyDeclaration(PredefinedType(Token(SyntaxKind.IntKeyword)), Identifier("Sign"))
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)))
.WithAttributeLists([GeneratedCodeAttribute(typeof(QuantityStructGenerator))])
Expand Down Expand Up @@ -173,7 +192,8 @@ public void GenerateCode(SourceProductionContext context)
SimpleBaseType(GenericName(Identifier("global::TedToolkit.Quantities.IQuantity"))
.WithTypeArgumentList(TypeArgumentList([
IdentifierName(quantity.Name),
IdentifierName(typeName.FullName)
IdentifierName(typeName.FullName),
IdentifierName(quantity.UnitName),
]))),
]))
.WithAttributeLists([GeneratedCodeAttribute(typeof(QuantityStructGenerator))])
Expand Down Expand Up @@ -258,7 +278,7 @@ public void GenerateCode(SourceProductionContext context)
.WithType(IdentifierName(quantity.UnitName))
]))
.WithAttributeLists([GeneratedCodeAttribute(typeof(QuantityStructGenerator))])
.WithXmlComment()
.WithXmlCommentInheritDoc((string?)null)
.WithBody(Block(
ReturnStatement(CastExpression(
IdentifierName(typeName.FullName), ParenthesizedExpression(
Expand Down Expand Up @@ -387,16 +407,36 @@ public void GenerateCode(SourceProductionContext context)

#endregion

#region Units

MethodDeclaration(IdentifierName(quantity.Name), Identifier("From"))
.WithModifiers(
TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
.WithAttributeLists([GeneratedCodeAttribute(typeof(QuantityStructGenerator))])
.WithXmlComment()
.WithParameterList(ParameterList(
[
Parameter(Identifier("value"))
.WithType(IdentifierName(typeName.FullName)),
Parameter(Identifier("unit"))
.WithType(IdentifierName(quantity.UnitName))
]))
.WithBody(Block(
ReturnStatement(ObjectCreationExpression(IdentifierName(quantity.Name))
.WithArgumentList(ArgumentList(
[
Argument(IdentifierName("value")),
Argument(IdentifierName("unit")),
]))))),

..quantity.Units.SelectMany(u =>
{
var info = data.Units[u];
var unitName = info.GetUnitName(data.Units.Values);
var parameterName = "@" + char.ToLowerInvariant(unitName[0]) + unitName[1..];
return (IEnumerable<MemberDeclarationSyntax>)
[
MethodDeclaration(
IdentifierName(quantity.Name),
Identifier("From" + unitName))
MethodDeclaration(IdentifierName(quantity.Name), Identifier("From" + unitName))
.WithModifiers(
TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
.WithAttributeLists([GeneratedCodeAttribute(typeof(QuantityStructGenerator))])
Expand Down Expand Up @@ -439,6 +479,9 @@ public void GenerateCode(SourceProductionContext context)
];
}),

#endregion


#region Conversions

ConversionOperatorDeclaration(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,11 @@ private static void GenerateItem(SourceProductionContext context, Dictionary<ISy
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
.WithAttributeLists([GeneratedCodeAttribute(typeof(ValidResultsGenerator))])
.WithMembers([
GenerateCreateTracker(IdentifierName(target.FullName).WithTypeParameterNames(parameterNames), true),
GenerateCreateTracker(IdentifierName(target.FullName).WithTypeParameterNames(parameterNames),
true),
GenerateCreateTracker(IdentifierName(data.GetName().FullName), false),
MethodDeclaration(IdentifierName(target.Name).WithTypeParameterNames(parameterNames), Identifier("ToValidResult"))
MethodDeclaration(IdentifierName(target.Name).WithTypeParameterNames(parameterNames),
Identifier("ToValidResult"))
.WithTypeParameterNames(parameterNames)
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
.WithAttributeLists([
Expand All @@ -169,13 +171,15 @@ private static void GenerateItem(SourceProductionContext context, Dictionary<ISy
]))
.WithExpressionBody(ArrowExpressionClause(InvocationExpression(MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(target.Name).WithTypeParameterNames(parameterNames), IdentifierName("Ok")))
IdentifierName(target.Name).WithTypeParameterNames(parameterNames),
IdentifierName("Ok")))
.WithArgumentList(ArgumentList(
[
Argument(IdentifierName("value"))
]))))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
..GenerateStaticMembers(members, dictionary, trackerName, baseDataSymbol, extensionMethods, parameterNames)
..GenerateStaticMembers(members, dictionary, trackerName, baseDataSymbol, extensionMethods,
parameterNames)
]),
ClassDeclaration(trackerName)
.WithTypeParameterNames(parameterNames)
Expand Down Expand Up @@ -282,6 +286,7 @@ private static IEnumerable<MemberDeclarationSyntax> GenerateStaticMembers(IReadO
.OfType<IMethodSymbol>()
.Where(p => !p.ReturnType.IsRefLikeType)
.Where(p => !p.IsStatic)
.Where(p => p.Name is not "<Clone>$")
.Where(p => !staticMethods.Contains(new MethodSignature(p))))
if (method.MethodKind is MethodKind.Ordinary)
yield return OrdinaryMethod(method, dictionary, trackerName, paramNames);
Expand Down Expand Up @@ -316,7 +321,8 @@ bool IsEffectivelyPublic(IMethodSymbol method)
}

private static IEnumerable<MemberDeclarationSyntax> GenerateMembers(IReadOnlyCollection<ISymbol> members,
Dictionary<ISymbol?, SimpleType> dictionary, string trackerName, ITypeSymbol? baseTypeSymbol, IReadOnlyCollection<ITypeParamName> paramNames)
Dictionary<ISymbol?, SimpleType> dictionary, string trackerName, ITypeSymbol? baseTypeSymbol,
IReadOnlyCollection<ITypeParamName> paramNames)
{
var propertyOrFieldNames = baseTypeSymbol?.GetMembers().OfType<IPropertySymbol>().Select(i => i.Name)
.Concat(baseTypeSymbol.GetMembers().OfType<IFieldSymbol>().Select(i => i.Name)).ToArray() ?? [];
Expand All @@ -334,7 +340,8 @@ private static IEnumerable<MemberDeclarationSyntax> GenerateMembers(IReadOnlyCol
.Where(p => !propertyOrFieldNames.Contains(p.Name))
.Where(p => !p.Type.IsRefLikeType))
yield return GenerateProperty(property.Type, property.Name, property.ContainingType,
dictionary, property.GetMethod is not null, property.SetMethod is not null, property.Parameters);
dictionary, property.GetMethod is not null, property.SetMethod is { IsInitOnly : false },
property.Parameters);

foreach (var field in members
.OfType<IFieldSymbol>()
Expand Down Expand Up @@ -365,7 +372,8 @@ private static MemberDeclarationSyntax OrdinaryMethod(IMethodSymbol method,
}

private static MemberDeclarationSyntax StaticOrdinaryMethod(IMethodSymbol method,
Dictionary<ISymbol?, SimpleType> dictionary, MethodParametersHelper.MethodType type, string trackerName, IReadOnlyCollection<ITypeParamName> paramNames)
Dictionary<ISymbol?, SimpleType> dictionary, MethodParametersHelper.MethodType type, string trackerName,
IReadOnlyCollection<ITypeParamName> paramNames)
{
return MethodParametersHelper.GenerateMethodByParameters(method,
[..method.Parameters.Select(p => new ParameterRelay(p))],
Expand Down
78 changes: 72 additions & 6 deletions src/libraries/TedToolkit.Quantities/IQuantity.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,57 @@
namespace TedToolkit.Quantities;
using System.Numerics;

namespace TedToolkit.Quantities;

/// <summary>
/// Quantity
/// </summary>
/// <typeparam name="TQuantity"></typeparam>
/// <typeparam name="TValue"></typeparam>
public interface IQuantity<TQuantity, out TValue> : IQuantity<TQuantity>
where TValue : struct, IConvertible
where TQuantity : IQuantity<TQuantity, TValue>
/// <typeparam name="TUnit"></typeparam>
public interface IQuantity<TQuantity, TValue, in TUnit> :
IQuantityQuantity<TQuantity>,
IQuantityValue<TValue>
where TValue : struct,
#if NET7_0_OR_GREATER
INumber<TValue>,
#endif
IConvertible
where TQuantity : struct, IQuantity<TQuantity, TValue, TUnit>
where TUnit : Enum
{
#if NET7_0_OR_GREATER
/// <summary/>
static abstract TQuantity From(TValue value, TUnit unit);
#endif

/// <summary>
///
/// </summary>
/// <param name="unit"></param>
/// <returns></returns>
TValue As(TUnit unit);

/// <summary>
///
/// </summary>
/// <param name="unit"></param>
/// <param name="format"></param>
/// <param name="formatProvider"></param>
/// <returns></returns>
string ToString(TUnit unit, string? format = null, IFormatProvider? formatProvider = null);
}

/// <summary>
///
/// </summary>
/// <typeparam name="TValue"></typeparam>
public interface IQuantityValue<out TValue> :
IQuantity
where TValue : struct,
#if NET7_0_OR_GREATER
INumber<TValue>,
#endif
IConvertible
{
/// <summary>
/// The Value. In the most case, you don't need it.
Expand All @@ -19,10 +63,32 @@ public interface IQuantity<TQuantity, out TValue> : IQuantity<TQuantity>
/// Quantity
/// </summary>
/// <typeparam name="TQuantity"></typeparam>
public interface IQuantity<TQuantity> :
public interface IQuantityQuantity<TQuantity> :
IQuantity,
IEquatable<TQuantity>,
IComparable<TQuantity>;
IComparable<TQuantity>
where TQuantity : struct, IQuantityQuantity<TQuantity>
{
#if NET7_0_OR_GREATER
/// <summary/>
static abstract TQuantity Zero { get; }

/// <summary/>
static abstract TQuantity One { get; }
#endif

/// <inheritdoc cref="Math.Abs(double)"/>
TQuantity Abs();

/// <inheritdoc cref="Math.Min(double, double)"/>
TQuantity Min(TQuantity val2);

/// <inheritdoc cref="Math.Max(double, double)"/>
TQuantity Max(TQuantity val2);

/// <inheritdoc cref="Math.Clamp(double, double, double)"/>
TQuantity Clamp(TQuantity min, TQuantity max);
}

/// <summary>
/// Quantity
Expand Down