Skip to content

Commit f0c3849

Browse files
committed
Conversion operators of a smart enum can be configured
1 parent 04099fa commit f0c3849

File tree

29 files changed

+1835
-33
lines changed

29 files changed

+1835
-33
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Thinktecture.CodeAnalysis;
2+
3+
public enum ConversionOperatorsGeneration
4+
{
5+
None = 0,
6+
Implicit = 1,
7+
Explicit = 2
8+
}

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/AllEnumSettings.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public sealed class AllEnumSettings : IEquatable<AllEnumSettings>, IKeyMemberSet
1414
public bool SkipToString { get; }
1515
public SwitchMapMethodsGeneration SwitchMethods { get; }
1616
public SwitchMapMethodsGeneration MapMethods { get; }
17+
public ConversionOperatorsGeneration ConversionFromKeyMemberType { get; }
18+
public ConversionOperatorsGeneration ConversionToKeyMemberType { get; }
1719

1820
public AllEnumSettings(AttributeData attribute)
1921
{
@@ -29,6 +31,8 @@ public AllEnumSettings(AttributeData attribute)
2931
SkipToString = attribute.FindSkipToString() ?? false;
3032
SwitchMethods = attribute.FindSwitchMethods();
3133
MapMethods = attribute.FindMapMethods();
34+
ConversionToKeyMemberType = attribute.FindConversionToKeyMemberType() ?? ConversionOperatorsGeneration.Implicit;
35+
ConversionFromKeyMemberType = attribute.FindConversionFromKeyMemberType() ?? ConversionOperatorsGeneration.Explicit;
3236

3337
// Comparison operators depend on the equality comparison operators
3438
if (ComparisonOperators > EqualityComparisonOperators)
@@ -58,7 +62,9 @@ public bool Equals(AllEnumSettings? other)
5862
&& SkipIFormattable == other.SkipIFormattable
5963
&& SkipToString == other.SkipToString
6064
&& SwitchMethods == other.SwitchMethods
61-
&& MapMethods == other.MapMethods;
65+
&& MapMethods == other.MapMethods
66+
&& ConversionToKeyMemberType == other.ConversionToKeyMemberType
67+
&& ConversionFromKeyMemberType == other.ConversionFromKeyMemberType;
6268
}
6369

6470
public override int GetHashCode()
@@ -77,6 +83,8 @@ public override int GetHashCode()
7783
hashCode = (hashCode * 397) ^ SkipToString.GetHashCode();
7884
hashCode = (hashCode * 397) ^ SwitchMethods.GetHashCode();
7985
hashCode = (hashCode * 397) ^ MapMethods.GetHashCode();
86+
hashCode = (hashCode * 397) ^ ConversionToKeyMemberType.GetHashCode();
87+
hashCode = (hashCode * 397) ^ ConversionFromKeyMemberType.GetHashCode();
8088

8189
return hashCode;
8290
}

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/EnumSettings.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace Thinktecture.CodeAnalysis.SmartEnums;
99
public bool SkipToString => _settings.SkipToString;
1010
public SwitchMapMethodsGeneration SwitchMethods => _settings.SwitchMethods;
1111
public SwitchMapMethodsGeneration MapMethods => _settings.MapMethods;
12+
public ConversionOperatorsGeneration ConversionToKeyMemberType => _settings.ConversionToKeyMemberType;
13+
public ConversionOperatorsGeneration ConversionFromKeyMemberType => _settings.ConversionFromKeyMemberType;
1214
public bool HasStructLayoutAttribute => _attributeInfo.HasStructLayoutAttribute;
1315
public string? KeyMemberEqualityComparerAccessor => _attributeInfo.KeyMemberEqualityComparerAccessor;
1416
public ImmutableArray<DesiredFactory> DesiredFactories => _attributeInfo.DesiredFactories;
@@ -30,6 +32,8 @@ public bool Equals(EnumSettings other)
3032
&& SkipToString == other.SkipToString
3133
&& SwitchMethods == other.SwitchMethods
3234
&& MapMethods == other.MapMethods
35+
&& ConversionToKeyMemberType == other.ConversionToKeyMemberType
36+
&& ConversionFromKeyMemberType == other.ConversionFromKeyMemberType
3337
&& HasStructLayoutAttribute == other.HasStructLayoutAttribute
3438
&& KeyMemberEqualityComparerAccessor == other.KeyMemberEqualityComparerAccessor
3539
&& DesiredFactories.SequenceEqual(other.DesiredFactories);
@@ -43,6 +47,8 @@ public override int GetHashCode()
4347
hashCode = (hashCode * 397) ^ SkipToString.GetHashCode();
4448
hashCode = (hashCode * 397) ^ SwitchMethods.GetHashCode();
4549
hashCode = (hashCode * 397) ^ MapMethods.GetHashCode();
50+
hashCode = (hashCode * 397) ^ ConversionFromKeyMemberType.GetHashCode();
51+
hashCode = (hashCode * 397) ^ ConversionToKeyMemberType.GetHashCode();
4652
hashCode = (hashCode * 397) ^ HasStructLayoutAttribute.GetHashCode();
4753
hashCode = (hashCode * 397) ^ (KeyMemberEqualityComparerAccessor?.GetHashCode() ?? 0);
4854
hashCode = (hashCode * 397) ^ DesiredFactories.ComputeHashCode();

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumCodeGenerator.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ private void GenerateEnum(CancellationToken cancellationToken)
184184
if (_state.KeyMember.IsString())
185185
GenerateValidateForReadOnlySpanOfChar(_state.KeyMember);
186186

187-
GenerateImplicitConversion(_state.KeyMember);
188-
GenerateExplicitConversion(_state.KeyMember);
187+
GenerateConversionToKeyType(_state.KeyMember);
188+
GenerateConversionFromKeyType(_state.KeyMember);
189189
}
190190

191191
GenerateEquals();
@@ -1002,20 +1002,20 @@ private void GenerateValidateForReadOnlySpanOfChar(IMemberState keyProperty)
10021002
#endif");
10031003
}
10041004

1005-
private void GenerateImplicitConversion(KeyMemberState keyProperty)
1005+
private void GenerateConversionToKeyType(KeyMemberState keyProperty)
10061006
{
1007-
if (keyProperty.IsInterface)
1007+
if (keyProperty.IsInterface || _state.Settings.ConversionToKeyMemberType == ConversionOperatorsGeneration.None)
10081008
return;
10091009

10101010
_sb.Append(@"
10111011
10121012
/// <summary>
1013-
/// Implicit conversion to the type ").AppendTypeForXmlComment(keyProperty).Append(@".
1013+
/// ").Append(_state.Settings.ConversionToKeyMemberType == ConversionOperatorsGeneration.Implicit ? "Implicit" : "Explicit").Append(" conversion to the type ").AppendTypeForXmlComment(keyProperty).Append(@".
10141014
/// </summary>
10151015
/// <param name=""item"">Item to covert.</param>
10161016
/// <returns>The ").AppendTypeForXmlComment(_state, (keyProperty.Name, ".")).Append(@" of provided <paramref name=""item""/> or <c>default</c> if <paramref name=""item""/> is <c>null</c>.</returns>
10171017
[return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull(""item"")]
1018-
public static implicit operator ").AppendTypeFullyQualifiedNullAnnotated(keyProperty).Append("(").AppendTypeFullyQualifiedNullAnnotated(_state).Append(@" item)
1018+
public static ").AppendConversionOperator(_state.Settings.ConversionToKeyMemberType).Append(" operator ").AppendTypeFullyQualifiedNullAnnotated(keyProperty).Append("(").AppendTypeFullyQualifiedNullAnnotated(_state).Append(@" item)
10191019
{");
10201020

10211021
if (_state.IsReferenceType)
@@ -1033,20 +1033,20 @@ private void GenerateImplicitConversion(KeyMemberState keyProperty)
10331033
}");
10341034
}
10351035

1036-
private void GenerateExplicitConversion(KeyMemberState keyProperty)
1036+
private void GenerateConversionFromKeyType(KeyMemberState keyProperty)
10371037
{
1038-
if (keyProperty.IsInterface)
1038+
if (keyProperty.IsInterface || _state.Settings.ConversionFromKeyMemberType == ConversionOperatorsGeneration.None)
10391039
return;
10401040

10411041
_sb.Append(@"
10421042
10431043
/// <summary>
1044-
/// Explicit conversion from the type ").AppendTypeForXmlComment(keyProperty).Append(@".
1044+
/// ").Append(_state.Settings.ConversionFromKeyMemberType == ConversionOperatorsGeneration.Implicit ? "Implicit" : "Explicit").Append(" conversion from the type ").AppendTypeForXmlComment(keyProperty).Append(@".
10451045
/// </summary>
10461046
/// <param name=""").Append(keyProperty.ArgumentName).Append(@""">Value to covert.</param>
10471047
/// <returns>An instance of ").AppendTypeForXmlComment(_state).Append(@" if the <paramref name=""").Append(keyProperty.ArgumentName).Append(@"""/> is a known item or implements <see cref=""Thinktecture.IValidatableEnum""/>.</returns>
10481048
[return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull(""").Append(keyProperty.ArgumentName).Append(@""")]
1049-
public static explicit operator ").AppendTypeFullyQualifiedNullAnnotated(_state).Append("(").AppendTypeFullyQualifiedNullAnnotated(keyProperty).Append(" ").AppendEscaped(keyProperty.ArgumentName).Append(@")
1049+
public static ").AppendConversionOperator(_state.Settings.ConversionFromKeyMemberType).Append(" operator ").AppendTypeFullyQualifiedNullAnnotated(_state).Append("(").AppendTypeFullyQualifiedNullAnnotated(keyProperty).Append(" ").AppendEscaped(keyProperty.ArgumentName).Append(@")
10501050
{
10511051
return ").AppendTypeFullyQualified(_state).Append(".").Append(Constants.Methods.GET).Append("(").AppendEscaped(keyProperty.ArgumentName).Append(@");
10521052
}");

src/Thinktecture.Runtime.Extensions.SourceGenerator/Extensions/AttributeDataExtensions.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,16 @@ public static SwitchMapMethodsGeneration FindMapMethods(this AttributeData attri
139139
return GetSwitchMapGeneration(attributeData, "MapMethods");
140140
}
141141

142+
public static ConversionOperatorsGeneration? FindConversionToKeyMemberType(this AttributeData attributeData)
143+
{
144+
return GetConversionOperatorsGeneration(attributeData, "ConversionToKeyMemberType");
145+
}
146+
147+
public static ConversionOperatorsGeneration? FindConversionFromKeyMemberType(this AttributeData attributeData)
148+
{
149+
return GetConversionOperatorsGeneration(attributeData, "ConversionFromKeyMemberType");
150+
}
151+
142152
public static SerializationFrameworks FindUseForSerialization(this AttributeData attributeData)
143153
{
144154
var frameworks = (SerializationFrameworks?)GetIntegerParameterValue(attributeData, "UseForSerialization");
@@ -231,6 +241,16 @@ private static SwitchMapMethodsGeneration GetSwitchMapGeneration(AttributeData a
231241
return generation.Value;
232242
}
233243

244+
private static ConversionOperatorsGeneration? GetConversionOperatorsGeneration(AttributeData attributeData, string name)
245+
{
246+
var generation = (ConversionOperatorsGeneration?)GetIntegerParameterValue(attributeData, name);
247+
248+
if (generation is null || !generation.Value.IsValid())
249+
return null;
250+
251+
return generation.Value;
252+
}
253+
234254
private static int? GetIntegerParameterValue(AttributeData attributeData, string name)
235255
{
236256
return (int?)attributeData.FindNamedAttribute(name).Value;

src/Thinktecture.Runtime.Extensions.SourceGenerator/Extensions/StringBuilderExtensions.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,25 @@ public static void GenerateStructLayoutAttributeIfRequired(this StringBuilder sb
1414
}
1515
}
1616

17+
public static StringBuilder AppendConversionOperator(
18+
this StringBuilder sb,
19+
ConversionOperatorsGeneration operatorsGeneration)
20+
{
21+
switch (operatorsGeneration)
22+
{
23+
case ConversionOperatorsGeneration.None:
24+
break;
25+
case ConversionOperatorsGeneration.Implicit:
26+
sb.Append("implicit");
27+
break;
28+
case ConversionOperatorsGeneration.Explicit:
29+
sb.Append("explicit");
30+
break;
31+
}
32+
33+
return sb;
34+
}
35+
1736
public static StringBuilder RenderAccessModifier(
1837
this StringBuilder sb,
1938
ValueObjectAccessModifier accessModifier)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Thinktecture;
2+
3+
/// <summary>
4+
/// Defines whether and how the implicit conversion operators should be generated.
5+
/// </summary>
6+
public enum ConversionOperatorsGeneration
7+
{
8+
/// <summary>
9+
/// No conversion operators will be generated.
10+
/// </summary>
11+
None = 0,
12+
13+
/// <summary>
14+
/// Conversions will be generated as implicit operators.
15+
/// </summary>
16+
Implicit = 1,
17+
18+
/// <summary>
19+
/// Conversions will be generated as explicit operators.
20+
/// </summary>
21+
Explicit = 2
22+
}

src/Thinktecture.Runtime.Extensions/SmartEnumAttribute.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,18 @@ public OperatorsGeneration EqualityComparisonOperators
132132
/// </summary>
133133
public SwitchMapMethodsGeneration MapMethods { get; set; }
134134

135+
/// <summary>
136+
/// Indication whether and how the generator should generate the conversion operators from enum type to <see cref="KeyMemberType"/>.
137+
/// Default is <see cref="ConversionOperatorsGeneration.Implicit"/>.
138+
/// </summary>
139+
public ConversionOperatorsGeneration ConversionToKeyMemberType { get; set; }
140+
141+
/// <summary>
142+
/// Indication whether and how the generator should generate the conversion operators from <see cref="KeyMemberType"/> to enum type.
143+
/// Default is <see cref="ConversionOperatorsGeneration.Explicit"/>.
144+
/// </summary>
145+
public ConversionOperatorsGeneration ConversionFromKeyMemberType { get; set; }
146+
135147
/// <summary>
136148
/// Initializes new instance of <see cref="SmartEnumAttribute{TKey}"/>.
137149
/// </summary>
@@ -140,5 +152,7 @@ public SmartEnumAttribute()
140152
KeyMemberType = typeof(TKey);
141153
KeyMemberAccessModifier = ValueObjectAccessModifier.Public;
142154
KeyMemberKind = ValueObjectMemberKind.Property;
155+
ConversionToKeyMemberType = ConversionOperatorsGeneration.Implicit;
156+
ConversionFromKeyMemberType = ConversionOperatorsGeneration.Explicit;
143157
}
144158
}

0 commit comments

Comments
 (0)