From 1ceec5c4919949879b3379a20084964960e96015 Mon Sep 17 00:00:00 2001 From: hadashiA Date: Thu, 1 Feb 2024 12:56:32 +0900 Subject: [PATCH 1/2] Support System.IFormattable/System.IParsable --- sandbox/FileGenerate/SimplePrimitive.cs | 2 +- .../FileGenerate.A.g.cs | 3 + .../FileGenerate.Aa.g.cs | 3 + .../FileGenerate.C.g.cs | 46 ++++++++++ .../FileGenerate.Cc.g.cs | 3 + src/UnitGenerator/ReferenceSymbols.cs | 23 +++++ src/UnitGenerator/SourceGenerator.cs | 88 ++++++++++++++++++- src/UnitGenerator/UnitGenerator.csproj | 2 +- 8 files changed, 165 insertions(+), 5 deletions(-) create mode 100644 src/UnitGenerator/ReferenceSymbols.cs diff --git a/sandbox/FileGenerate/SimplePrimitive.cs b/sandbox/FileGenerate/SimplePrimitive.cs index b98deef..a17c135 100644 --- a/sandbox/FileGenerate/SimplePrimitive.cs +++ b/sandbox/FileGenerate/SimplePrimitive.cs @@ -12,7 +12,7 @@ public readonly partial struct B { } - [UnitOf(typeof(int), UnitGenerateOptions.Comparable | UnitGenerateOptions.ArithmeticOperator | UnitGenerateOptions.ValueArithmeticOperator)] + [UnitOf(typeof(int), UnitGenerateOptions.Comparable | UnitGenerateOptions.ArithmeticOperator | UnitGenerateOptions.ValueArithmeticOperator | UnitGenerateOptions.ParseMethod)] public readonly partial struct C { } diff --git a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.A.g.cs b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.A.g.cs index b547431..4842564 100644 --- a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.A.g.cs +++ b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.A.g.cs @@ -15,6 +15,7 @@ readonly partial struct A #if NET7_0_OR_GREATER , IEqualityOperators #endif + , IFormattable { readonly int value; @@ -73,6 +74,8 @@ public override int GetHashCode() public override string ToString() => value.ToString(); + public string ToString(string? format, IFormatProvider? formatProvider) => value.ToString(format, formatProvider); + // Default private class ATypeConverter : System.ComponentModel.TypeConverter diff --git a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.Aa.g.cs b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.Aa.g.cs index cbff044..f92ae9d 100644 --- a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.Aa.g.cs +++ b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.Aa.g.cs @@ -15,6 +15,7 @@ readonly partial struct Aa #if NET7_0_OR_GREATER , IEqualityOperators #endif + , IFormattable { readonly int value; @@ -73,6 +74,8 @@ public override int GetHashCode() public override string ToString() => value.ToString(); + public string ToString(string? format, IFormatProvider? formatProvider) => value.ToString(format, formatProvider); + // Default private class AaTypeConverter : System.ComponentModel.TypeConverter diff --git a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.C.g.cs b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.C.g.cs index 5807c9c..d5f117e 100644 --- a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.C.g.cs +++ b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.C.g.cs @@ -19,6 +19,10 @@ readonly partial struct C #if NET7_0_OR_GREATER , IComparisonOperators #endif +#if NET7_0_OR_GREATER + , IParsable +#endif + , IFormattable #if NET7_0_OR_GREATER , IAdditionOperators , ISubtractionOperators @@ -87,6 +91,48 @@ public override int GetHashCode() public override string ToString() => value.ToString(); + public string ToString(string? format, IFormatProvider? formatProvider) => value.ToString(format, formatProvider); + + // UnitGenerateOptions.ParseMethod + + public static C Parse(string s) + { + return new C(int.Parse(s)); + } + + public static bool TryParse(string s, out C result) + { + if (int.TryParse(s, out var r)) + { + result = new C(r); + return true; + } + else + { + result = default(C); + return false; + } + } + + public static C Parse(string s, IFormatProvider? provider) + { + return new C(int.Parse(s)); + } + + public static bool TryParse(string s, IFormatProvider? provider, out C result) + { + if (int.TryParse(s, out var r)) + { + result = new C(r); + return true; + } + else + { + result = default(C); + return false; + } + } + // UnitGenerateOptions.ArithmeticOperator public static C operator +(C x, C y) diff --git a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.Cc.g.cs b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.Cc.g.cs index f77081e..4d4a367 100644 --- a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.Cc.g.cs +++ b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.Cc.g.cs @@ -19,6 +19,7 @@ readonly partial struct Cc #if NET7_0_OR_GREATER , IComparisonOperators #endif + , IFormattable #if NET7_0_OR_GREATER , IAdditionOperators , ISubtractionOperators @@ -87,6 +88,8 @@ public override int GetHashCode() public override string ToString() => value.ToString(); + public string ToString(string? format, IFormatProvider? formatProvider) => value.ToString(format, formatProvider); + // UnitGenerateOptions.ArithmeticOperator public static Cc operator +(Cc x, Cc y) diff --git a/src/UnitGenerator/ReferenceSymbols.cs b/src/UnitGenerator/ReferenceSymbols.cs new file mode 100644 index 0000000..4cfad4a --- /dev/null +++ b/src/UnitGenerator/ReferenceSymbols.cs @@ -0,0 +1,23 @@ +using System; +using Microsoft.CodeAnalysis; + +namespace UnitGenerator; + +class ReferenceSymbols +{ + public static ReferenceSymbols Create(Compilation compilation) + { + return new ReferenceSymbols + { + IParsableInterface = compilation.GetTypeByMetadataName("System.IParsable`1")!, + IFormattableInterface = compilation.GetTypeByMetadataName("System.IFormattable")!, + GuidType = compilation.GetTypeByMetadataName("System.Guid"), + UlidType = compilation.GetTypeByMetadataName("System.Ulid"), + }; + } + + public INamedTypeSymbol IParsableInterface { get; private set; } = default!; + public INamedTypeSymbol IFormattableInterface { get; private set; } = default!; + public INamedTypeSymbol? UlidType { get; private set; } + public INamedTypeSymbol? GuidType { get; private set; } +} \ No newline at end of file diff --git a/src/UnitGenerator/SourceGenerator.cs b/src/UnitGenerator/SourceGenerator.cs index a9a74c1..9a9a2ca 100644 --- a/src/UnitGenerator/SourceGenerator.cs +++ b/src/UnitGenerator/SourceGenerator.cs @@ -25,13 +25,18 @@ public void Execute(GeneratorExecutionContext context) var receiver = context.SyntaxReceiver as SyntaxReceiver; if (receiver == null) return; + var symbols = ReferenceSymbols.Create(context.Compilation); var list = new List<(StructDeclarationSyntax, UnitOfAttributeProperty)>(); foreach (var (type, attr, targetType) in receiver.Targets) { var model = context.Compilation.GetSemanticModel(type.SyntaxTree); // retrieve attribute parameter - var prop = new UnitOfAttributeProperty { ArithmeticOperators = UnitArithmeticOperators.All }; + var prop = new UnitOfAttributeProperty + { + Symbols = symbols, + ArithmeticOperators = UnitArithmeticOperators.All + }; if(targetType is not null) { @@ -276,6 +281,21 @@ readonly partial struct {{unitTypeName}} #endif """); } + if (prop.HasFlag(UnitGenerateOptions.ParseMethod) && prop.HasIParsableInterface()) + { + sb.AppendLine($$""" +#if NET7_0_OR_GREATER + , IParsable<{{unitTypeName}}> +#endif +"""); + } + if (prop.HasIFormattableInterface()) + { + sb.AppendLine($$""" + , IFormattable +"""); + } + if (prop.HasFlag(UnitGenerateOptions.ArithmeticOperator)) { sb.AppendLine("#if NET7_0_OR_GREATER"); @@ -453,6 +473,14 @@ public override int GetHashCode() } } + if (prop.HasIParsableInterface()) + { + sb.AppendLine($$""" + public string ToString(string? format, IFormatProvider? formatProvider) => value.ToString(format, formatProvider); + +"""); + } + if (prop.IsGuid()) { sb.AppendLine($$""" @@ -543,7 +571,34 @@ public static bool TryParse(string s, out {{unitTypeName}} result) """); } + + if (prop.HasIParsableInterface()) + { + sb.AppendLine($$""" + public static {{unitTypeName}} Parse(string s, IFormatProvider? provider) + { + return new {{unitTypeName}}({{innerTypeName}}.Parse(s)); + } + + public static bool TryParse(string s, IFormatProvider? provider, out {{unitTypeName}} result) + { + if ({{innerTypeName}}.TryParse(s, out var r)) + { + result = new {{unitTypeName}}(r); + return true; + } + else + { + result = default({{unitTypeName}}); + return false; + } + } + +"""); + + } } + if (prop.HasFlag(UnitGenerateOptions.MinMaxMethod)) { sb.AppendLine($$""" @@ -1049,6 +1104,7 @@ public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext co struct UnitOfAttributeProperty { + public ReferenceSymbols Symbols { get; set; } public ITypeSymbol Type { get; set; } public UnitGenerateOptions Options { get; set; } public UnitArithmeticOperators ArithmeticOperators { get; set; } @@ -1057,8 +1113,8 @@ struct UnitOfAttributeProperty public bool IsString() => TypeName is "string"; public bool IsBool() => TypeName is "bool"; - public bool IsUlid() => TypeName is "Ulid" or "System.Ulid"; - public bool IsGuid() => TypeName is "Guid" or "System.Guid"; + public bool IsUlid() => SymbolEqualityComparer.Default.Equals(Type, Symbols.UlidType); + public bool IsGuid() => SymbolEqualityComparer.Default.Equals(Type, Symbols.GuidType); public bool HasFlag(UnitGenerateOptions options) => Options.HasFlag(options); @@ -1072,6 +1128,32 @@ public bool HasValueArithmeticOperator(UnitArithmeticOperators op) return HasFlag(UnitGenerateOptions.ValueArithmeticOperator) && ArithmeticOperators.HasFlag(op); } + public bool HasIParsableInterface() + { + foreach (var x in Type.AllInterfaces) + { + if (x.IsGenericType && + SymbolEqualityComparer.Default.Equals(x.ConstructedFrom, Symbols.IParsableInterface) && + SymbolEqualityComparer.Default.Equals(x.TypeArguments[0], Type)) + { + return true; + } + } + return false; + } + + public bool HasIFormattableInterface() + { + foreach (var x in Type.AllInterfaces) + { + if (SymbolEqualityComparer.Default.Equals(x, Symbols.IFormattableInterface)) + { + return true; + } + } + return false; + } + public DbType GetDbType() { return TypeName switch diff --git a/src/UnitGenerator/UnitGenerator.csproj b/src/UnitGenerator/UnitGenerator.csproj index f78bdf1..3c85e2c 100644 --- a/src/UnitGenerator/UnitGenerator.csproj +++ b/src/UnitGenerator/UnitGenerator.csproj @@ -2,7 +2,7 @@ netstandard2.0 - preview + 12 enable $(NoWarn);CS1591 true From a76984967c785ae2e4494abf8a303e4c2d85adf8 Mon Sep 17 00:00:00 2001 From: hadashiA Date: Thu, 1 Feb 2024 15:27:29 +0900 Subject: [PATCH 2/2] Support ISpanFormattable,ISpanParsable,IUtf8SpanFormattable,IUtf8SpanParsable --- .../FileGenerate.A.g.cs | 13 +- .../FileGenerate.B.g.cs | 4 +- .../FileGenerate.C.g.cs | 69 ++++- src/UnitGenerator/ReferenceSymbols.cs | 20 +- src/UnitGenerator/SourceGenerator.cs | 275 +++++++++++++----- 5 files changed, 283 insertions(+), 98 deletions(-) diff --git a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.A.g.cs b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.A.g.cs index 4842564..1097a85 100644 --- a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.A.g.cs +++ b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.A.g.cs @@ -12,10 +12,13 @@ namespace FileGenerate [System.ComponentModel.TypeConverter(typeof(ATypeConverter))] readonly partial struct A : IEquatable -#if NET7_0_OR_GREATER - , IEqualityOperators -#endif , IFormattable +#if NET6_0_OR_GREATER + , ISpanFormattable +#endif +#if NET8_0_OR_GREATER + , IEqualityOperators +#endif { readonly int value; @@ -76,6 +79,10 @@ public override int GetHashCode() public string ToString(string? format, IFormatProvider? formatProvider) => value.ToString(format, formatProvider); +#if NET6_0_OR_GREATER + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => + ((ISpanFormattable)value).TryFormat(destination, out charsWritten, format, provider); +#endif // Default private class ATypeConverter : System.ComponentModel.TypeConverter diff --git a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.B.g.cs b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.B.g.cs index d58b9b0..fccbdd2 100644 --- a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.B.g.cs +++ b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.B.g.cs @@ -12,9 +12,9 @@ namespace FileGenerate [System.ComponentModel.TypeConverter(typeof(BTypeConverter))] readonly partial struct B : IEquatable -#if NET7_0_OR_GREATER +#if NET8_0_OR_GREATER , IEqualityOperators -#endif +#endif { readonly string value; diff --git a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.C.g.cs b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.C.g.cs index d5f117e..82b70d9 100644 --- a/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.C.g.cs +++ b/sandbox/Generated/UnitGenerator/UnitGenerator.SourceGenerator/FileGenerate.C.g.cs @@ -12,18 +12,15 @@ namespace FileGenerate [System.ComponentModel.TypeConverter(typeof(CTypeConverter))] readonly partial struct C : IEquatable -#if NET7_0_OR_GREATER - , IEqualityOperators -#endif , IComparable -#if NET7_0_OR_GREATER - , IComparisonOperators + , IFormattable +#if NET6_0_OR_GREATER + , ISpanFormattable #endif #if NET7_0_OR_GREATER + , IComparisonOperators , IParsable -#endif - , IFormattable -#if NET7_0_OR_GREATER + , ISpanParsable , IAdditionOperators , ISubtractionOperators , IMultiplyOperators @@ -32,6 +29,10 @@ readonly partial struct C , IUnaryNegationOperators , IIncrementOperators , IDecrementOperators +#endif +#if NET8_0_OR_GREATER + , IEqualityOperators + , IUtf8SpanParsable #endif { readonly int value; @@ -93,6 +94,10 @@ public override int GetHashCode() public string ToString(string? format, IFormatProvider? formatProvider) => value.ToString(format, formatProvider); +#if NET6_0_OR_GREATER + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => + ((ISpanFormattable)value).TryFormat(destination, out charsWritten, format, provider); +#endif // UnitGenerateOptions.ParseMethod public static C Parse(string s) @@ -114,14 +119,15 @@ public static bool TryParse(string s, out C result) } } +#if NET7_0_OR_GREATER public static C Parse(string s, IFormatProvider? provider) { - return new C(int.Parse(s)); + return new C(int.Parse(s, provider)); } public static bool TryParse(string s, IFormatProvider? provider, out C result) { - if (int.TryParse(s, out var r)) + if (int.TryParse(s, provider, out var r)) { result = new C(r); return true; @@ -132,6 +138,49 @@ public static bool TryParse(string s, IFormatProvider? provider, out C result) return false; } } +#endif + +#if NET7_0_OR_GREATER + public static C Parse(ReadOnlySpan s, IFormatProvider? provider) + { + return new C(int.Parse(s, provider)); + } + + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out C result) + { + if (int.TryParse(s, provider, out var r)) + { + result = new C(r); + return true; + } + else + { + result = default(C); + return false; + } + } +#endif + +#if NET8_0_OR_GREATER + public static C Parse(ReadOnlySpan utf8Text, IFormatProvider? provider) + { + return new C(int.Parse(utf8Text, provider)); + } + + public static bool TryParse(ReadOnlySpan utf8Text, IFormatProvider? provider, out C result) + { + if (int.TryParse(utf8Text, provider, out var r)) + { + result = new C(r); + return true; + } + else + { + result = default(C); + return false; + } + } +#endif // UnitGenerateOptions.ArithmeticOperator diff --git a/src/UnitGenerator/ReferenceSymbols.cs b/src/UnitGenerator/ReferenceSymbols.cs index 4cfad4a..fd417ca 100644 --- a/src/UnitGenerator/ReferenceSymbols.cs +++ b/src/UnitGenerator/ReferenceSymbols.cs @@ -9,15 +9,23 @@ public static ReferenceSymbols Create(Compilation compilation) { return new ReferenceSymbols { - IParsableInterface = compilation.GetTypeByMetadataName("System.IParsable`1")!, - IFormattableInterface = compilation.GetTypeByMetadataName("System.IFormattable")!, - GuidType = compilation.GetTypeByMetadataName("System.Guid"), + GuidType = compilation.GetTypeByMetadataName("System.Guid")!, UlidType = compilation.GetTypeByMetadataName("System.Ulid"), + FormattableInterface = compilation.GetTypeByMetadataName("System.IFormattable")!, + ParsableInterface = compilation.GetTypeByMetadataName("System.IParsable`1"), + SpanFormattableInterface = compilation.GetTypeByMetadataName("System.ISpanFormattable"), + SpanParsableInterface = compilation.GetTypeByMetadataName("System.ISpanParsable`1"), + Utf8SpanFormattableInterface = compilation.GetTypeByMetadataName("System.IUtf8SpanFormattable"), + Utf8SpanParsableInterface = compilation.GetTypeByMetadataName("System.IUtf8SpanParsable`1"), }; } - public INamedTypeSymbol IParsableInterface { get; private set; } = default!; - public INamedTypeSymbol IFormattableInterface { get; private set; } = default!; + public INamedTypeSymbol GuidType { get; private set; } = default!; public INamedTypeSymbol? UlidType { get; private set; } - public INamedTypeSymbol? GuidType { get; private set; } + public INamedTypeSymbol FormattableInterface { get; private set; } = default!; + public INamedTypeSymbol? ParsableInterface { get; private set; } + public INamedTypeSymbol? SpanFormattableInterface { get; private set; } + public INamedTypeSymbol? SpanParsableInterface { get; private set; } + public INamedTypeSymbol? Utf8SpanFormattableInterface { get; private set; } + public INamedTypeSymbol? Utf8SpanParsableInterface { get; private set; } } \ No newline at end of file diff --git a/src/UnitGenerator/SourceGenerator.cs b/src/UnitGenerator/SourceGenerator.cs index 9a9a2ca..ffa0a13 100644 --- a/src/UnitGenerator/SourceGenerator.cs +++ b/src/UnitGenerator/SourceGenerator.cs @@ -34,7 +34,7 @@ public void Execute(GeneratorExecutionContext context) // retrieve attribute parameter var prop = new UnitOfAttributeProperty { - Symbols = symbols, + ReferenceSymbols = symbols, ArithmeticOperators = UnitArithmeticOperators.All }; @@ -263,86 +263,127 @@ namespace {{ns}} """); } + var anyPlatformInterfaces = new List(); + var net6Interfaces = new List(); + var net7Interfaces = new List(); + var net8Interfaces = new List + { + $"IEqualityOperators<{unitTypeName}, {unitTypeName}, bool>" + }; + sb.AppendLine($$""" [System.ComponentModel.TypeConverter(typeof({{unitTypeName}}TypeConverter))] readonly partial struct {{unitTypeName}} : IEquatable<{{unitTypeName}}> -#if NET7_0_OR_GREATER - , IEqualityOperators<{{unitTypeName}}, {{unitTypeName}}, bool> -#endif """); if (prop.HasFlag(UnitGenerateOptions.Comparable) && !prop.HasFlag(UnitGenerateOptions.WithoutComparisonOperator)) { - sb.AppendLine($$""" - , IComparable<{{unitTypeName}}> -#if NET7_0_OR_GREATER - , IComparisonOperators<{{unitTypeName}}, {{unitTypeName}}, bool> -#endif -"""); + anyPlatformInterfaces.Add($"IComparable<{unitTypeName}>"); + net7Interfaces.Add($"IComparisonOperators<{unitTypeName}, {unitTypeName}, bool>"); } - if (prop.HasFlag(UnitGenerateOptions.ParseMethod) && prop.HasIParsableInterface()) + if (prop.HasFormattableInterface()) { - sb.AppendLine($$""" -#if NET7_0_OR_GREATER - , IParsable<{{unitTypeName}}> -#endif -"""); + anyPlatformInterfaces.Add("IFormattable"); } - if (prop.HasIFormattableInterface()) + if (prop.HasSpanFormattableInterface()) { - sb.AppendLine($$""" - , IFormattable -"""); + net6Interfaces.Add($"ISpanFormattable"); + } + if (prop.HasUtf8SpanFormattableInterface()) + { + net8Interfaces.Add($"IUtf8SpanFormattable"); + } + if (prop.HasFlag(UnitGenerateOptions.ParseMethod)) + { + if (prop.HasParsableInterface()) + { + net7Interfaces.Add($"IParsable<{unitTypeName}>"); + } + if (prop.HasSpanParsableInterface()) + { + net7Interfaces.Add($"ISpanParsable<{unitTypeName}>"); + } + if (prop.HasUtf8SpanParsableInterface()) + { + net8Interfaces.Add($"IUtf8SpanParsable<{unitTypeName}>"); + } } if (prop.HasFlag(UnitGenerateOptions.ArithmeticOperator)) { - sb.AppendLine("#if NET7_0_OR_GREATER"); if (prop.HasArithmeticOperator(UnitArithmeticOperators.Addition)) { - sb.AppendLine($$""" - , IAdditionOperators<{{unitTypeName}}, {{unitTypeName}}, {{unitTypeName}}> -"""); + net7Interfaces.Add($"IAdditionOperators<{unitTypeName}, {unitTypeName}, {unitTypeName}>"); } if (prop.HasArithmeticOperator(UnitArithmeticOperators.Subtraction)) { - sb.AppendLine($$""" - , ISubtractionOperators<{{unitTypeName}}, {{unitTypeName}}, {{unitTypeName}}> -"""); + net7Interfaces.Add($"ISubtractionOperators<{unitTypeName}, {unitTypeName}, {unitTypeName}>"); } if (prop.HasArithmeticOperator(UnitArithmeticOperators.Multiply)) { - sb.AppendLine($$""" - , IMultiplyOperators<{{unitTypeName}}, {{unitTypeName}}, {{unitTypeName}}> -"""); + net7Interfaces.Add($"IMultiplyOperators<{unitTypeName}, {unitTypeName}, {unitTypeName}>"); } if (prop.HasArithmeticOperator(UnitArithmeticOperators.Division)) { - sb.AppendLine($$""" - , IDivisionOperators<{{unitTypeName}}, {{unitTypeName}}, {{unitTypeName}}> -"""); + net7Interfaces.Add($"IDivisionOperators<{unitTypeName}, {unitTypeName}, {unitTypeName}>"); } if (prop.HasArithmeticOperator(UnitArithmeticOperators.Multiply) || prop.HasArithmeticOperator(UnitArithmeticOperators.Division)) { - sb.AppendLine($$""" - , IUnaryPlusOperators<{{unitTypeName}}, {{unitTypeName}}> - , IUnaryNegationOperators<{{unitTypeName}}, {{unitTypeName}}> -"""); + net7Interfaces.Add($"IUnaryPlusOperators<{unitTypeName}, {unitTypeName}>"); + net7Interfaces.Add($"IUnaryNegationOperators<{unitTypeName}, {unitTypeName}>"); } if (prop.HasArithmeticOperator(UnitArithmeticOperators.Increment)) { - sb.AppendLine($$""" - , IIncrementOperators<{{unitTypeName}}> -"""); + net7Interfaces.Add($"IIncrementOperators<{unitTypeName}>"); } if (prop.HasArithmeticOperator(UnitArithmeticOperators.Decrement)) { - sb.AppendLine($$""" - , IDecrementOperators<{{unitTypeName}}> -"""); + net7Interfaces.Add($"IDecrementOperators<{unitTypeName}>"); } + } + + foreach (var interfaceName in anyPlatformInterfaces) + { + sb.AppendLine($" , {interfaceName}"); + } + if (net6Interfaces.Count > 0) + { + sb.AppendLine("#if NET6_0_OR_GREATER"); + } + foreach (var interfaceName in net6Interfaces) + { + sb.AppendLine($" , {interfaceName}"); + } + if (net6Interfaces.Count > 0) + { + sb.AppendLine("#endif"); + } + + if (net7Interfaces.Count > 0) + { + sb.AppendLine("#if NET7_0_OR_GREATER"); + } + foreach (var interfaceName in net7Interfaces) + { + sb.AppendLine($" , {interfaceName}"); + } + if (net7Interfaces.Count > 0) + { + sb.AppendLine("#endif"); + } + + if (net8Interfaces.Count > 0) + { + sb.AppendLine("#if NET8_0_OR_GREATER"); + } + foreach (var interfaceName in net8Interfaces) + { + sb.AppendLine($" , {interfaceName}"); + } + if (net8Interfaces.Count > 0) + { sb.AppendLine("#endif"); } @@ -473,11 +514,30 @@ public override int GetHashCode() } } - if (prop.HasIParsableInterface()) + if (prop.HasFormattableInterface()) { - sb.AppendLine($$""" + sb.AppendLine(""" public string ToString(string? format, IFormatProvider? formatProvider) => value.ToString(format, formatProvider); +"""); + } + if (prop.HasSpanFormattableInterface()) + { + sb.AppendLine(""" +#if NET6_0_OR_GREATER + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => + ((ISpanFormattable)value).TryFormat(destination, out charsWritten, format, provider); +#endif +"""); + } + if (prop.HasUtf8SpanFormattableInterface()) + { + sb.AppendLine(""" +#if NET8_0_OR_GREATER + public bool TryFormat (Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) => + ((IUtf8SpanFormattable)value).TryFormat(utf8Destination, out bytesWritten, format, provider); +#endif + """); } @@ -572,17 +632,18 @@ public static bool TryParse(string s, out {{unitTypeName}} result) """); } - if (prop.HasIParsableInterface()) + if (prop.HasParsableInterface()) { sb.AppendLine($$""" +#if NET7_0_OR_GREATER public static {{unitTypeName}} Parse(string s, IFormatProvider? provider) { - return new {{unitTypeName}}({{innerTypeName}}.Parse(s)); + return new {{unitTypeName}}({{innerTypeName}}.Parse(s, provider)); } public static bool TryParse(string s, IFormatProvider? provider, out {{unitTypeName}} result) { - if ({{innerTypeName}}.TryParse(s, out var r)) + if ({{innerTypeName}}.TryParse(s, provider, out var r)) { result = new {{unitTypeName}}(r); return true; @@ -593,9 +654,62 @@ public static bool TryParse(string s, IFormatProvider? provider, out {{unitTypeN return false; } } +#endif + +"""); + } + if (prop.HasSpanParsableInterface()) + { + sb.AppendLine($$""" +#if NET7_0_OR_GREATER + public static {{unitTypeName}} Parse(ReadOnlySpan s, IFormatProvider? provider) + { + return new {{unitTypeName}}({{innerTypeName}}.Parse(s, provider)); + } + + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out {{unitTypeName}} result) + { + if ({{innerTypeName}}.TryParse(s, provider, out var r)) + { + result = new {{unitTypeName}}(r); + return true; + } + else + { + result = default({{unitTypeName}}); + return false; + } + } +#endif + +"""); + } + + if (prop.HasUtf8SpanParsableInterface()) + { + sb.AppendLine($$""" +#if NET8_0_OR_GREATER + public static {{unitTypeName}} Parse(ReadOnlySpan utf8Text, IFormatProvider? provider) + { + return new {{unitTypeName}}({{innerTypeName}}.Parse(utf8Text, provider)); + } + + public static bool TryParse(ReadOnlySpan utf8Text, IFormatProvider? provider, out {{unitTypeName}} result) + { + if ({{innerTypeName}}.TryParse(utf8Text, provider, out var r)) + { + result = new {{unitTypeName}}(r); + return true; + } + else + { + result = default({{unitTypeName}}); + return false; + } + } +#endif """); - } } @@ -1104,7 +1218,7 @@ public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext co struct UnitOfAttributeProperty { - public ReferenceSymbols Symbols { get; set; } + public ReferenceSymbols ReferenceSymbols { get; set; } public ITypeSymbol Type { get; set; } public UnitGenerateOptions Options { get; set; } public UnitArithmeticOperators ArithmeticOperators { get; set; } @@ -1113,8 +1227,8 @@ struct UnitOfAttributeProperty public bool IsString() => TypeName is "string"; public bool IsBool() => TypeName is "bool"; - public bool IsUlid() => SymbolEqualityComparer.Default.Equals(Type, Symbols.UlidType); - public bool IsGuid() => SymbolEqualityComparer.Default.Equals(Type, Symbols.GuidType); + public bool IsUlid() => SymbolEqualityComparer.Default.Equals(Type, ReferenceSymbols.UlidType); + public bool IsGuid() => SymbolEqualityComparer.Default.Equals(Type, ReferenceSymbols.GuidType); public bool HasFlag(UnitGenerateOptions options) => Options.HasFlag(options); @@ -1128,31 +1242,12 @@ public bool HasValueArithmeticOperator(UnitArithmeticOperators op) return HasFlag(UnitGenerateOptions.ValueArithmeticOperator) && ArithmeticOperators.HasFlag(op); } - public bool HasIParsableInterface() - { - foreach (var x in Type.AllInterfaces) - { - if (x.IsGenericType && - SymbolEqualityComparer.Default.Equals(x.ConstructedFrom, Symbols.IParsableInterface) && - SymbolEqualityComparer.Default.Equals(x.TypeArguments[0], Type)) - { - return true; - } - } - return false; - } - - public bool HasIFormattableInterface() - { - foreach (var x in Type.AllInterfaces) - { - if (SymbolEqualityComparer.Default.Equals(x, Symbols.IFormattableInterface)) - { - return true; - } - } - return false; - } + public bool HasFormattableInterface() => IsImplemented(ReferenceSymbols.FormattableInterface); + public bool HasParsableInterface() => ReferenceSymbols.ParsableInterface != null && IsImplementedGenericSelfType(ReferenceSymbols.ParsableInterface); + public bool HasSpanFormattableInterface() => ReferenceSymbols.SpanFormattableInterface != null && IsImplemented(ReferenceSymbols.SpanFormattableInterface); + public bool HasSpanParsableInterface() => ReferenceSymbols.SpanParsableInterface != null && IsImplementedGenericSelfType(ReferenceSymbols.SpanParsableInterface); + public bool HasUtf8SpanFormattableInterface() => ReferenceSymbols.Utf8SpanFormattableInterface != null && IsImplemented(ReferenceSymbols.Utf8SpanFormattableInterface); + public bool HasUtf8SpanParsableInterface() => ReferenceSymbols.ParsableInterface != null && IsImplementedGenericSelfType(ReferenceSymbols.ParsableInterface); public DbType GetDbType() { @@ -1202,6 +1297,32 @@ public bool IsSupportUtf8Formatter() _ => false }; } + + bool IsImplemented(INamedTypeSymbol interfaceSymbol) + { + foreach (var x in Type.AllInterfaces) + { + if (SymbolEqualityComparer.Default.Equals(x, interfaceSymbol)) + { + return true; + } + } + return false; + } + + bool IsImplementedGenericSelfType(INamedTypeSymbol interfaceSymbol) + { + foreach (var x in Type.AllInterfaces) + { + if (x.IsGenericType && + SymbolEqualityComparer.Default.Equals(x.ConstructedFrom, interfaceSymbol) && + SymbolEqualityComparer.Default.Equals(x.TypeArguments[0], Type)) + { + return true; + } + } + return false; + } } class SyntaxReceiver : ISyntaxReceiver