diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/InterfaceCodeGeneratorFactory.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/InterfaceCodeGeneratorFactory.cs index b1c6d06f..9c0a1e34 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/InterfaceCodeGeneratorFactory.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/InterfaceCodeGeneratorFactory.cs @@ -29,7 +29,8 @@ public bool Equals(ICodeGeneratorFactory other) public static class InterfaceCodeGeneratorFactory { - private static readonly ICodeGeneratorFactory _parsable = new InterfaceCodeGeneratorFactory(ParsableCodeGenerator.Default); + private static readonly ICodeGeneratorFactory _parsableForValueObject = new InterfaceCodeGeneratorFactory(ParsableCodeGenerator.ForValueObject); + private static readonly ICodeGeneratorFactory _parsableForEnum = new InterfaceCodeGeneratorFactory(ParsableCodeGenerator.ForEnum); private static readonly ICodeGeneratorFactory _parsableForValidatableEnum = new InterfaceCodeGeneratorFactory(ParsableCodeGenerator.ForValidatableEnum); private static readonly ICodeGeneratorFactory _comparable = new InterfaceCodeGeneratorFactory(ComparableCodeGenerator.Default); @@ -40,9 +41,13 @@ public static ICodeGeneratorFactory Comparable(stri return String.IsNullOrWhiteSpace(comparerAccessor) ? _comparable : new InterfaceCodeGeneratorFactory(new ComparableCodeGenerator(comparerAccessor)); } - public static ICodeGeneratorFactory Parsable(bool forValidatableEnum) + public static ICodeGeneratorFactory Parsable( + bool forEnum, + bool forValidatableEnum) { - return forValidatableEnum ? _parsableForValidatableEnum : _parsable; + return forEnum + ? forValidatableEnum ? _parsableForValidatableEnum : _parsableForEnum + : _parsableForValueObject; } public static bool EqualityComparison( diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ParsableCodeGenerator.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ParsableCodeGenerator.cs index b3175157..04e4d478 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ParsableCodeGenerator.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ParsableCodeGenerator.cs @@ -4,16 +4,21 @@ namespace Thinktecture.CodeAnalysis; public sealed class ParsableCodeGenerator : IInterfaceCodeGenerator { - public static readonly IInterfaceCodeGenerator Default = new ParsableCodeGenerator(false); - public static readonly IInterfaceCodeGenerator ForValidatableEnum = new ParsableCodeGenerator(true); + public static readonly IInterfaceCodeGenerator ForValueObject = new ParsableCodeGenerator(false, false); + public static readonly IInterfaceCodeGenerator ForEnum = new ParsableCodeGenerator(true, false); + public static readonly IInterfaceCodeGenerator ForValidatableEnum = new ParsableCodeGenerator(true, true); + private readonly bool _isForEnum; private readonly bool _isForValidatableEnum; public string CodeGeneratorName => "Parsable-CodeGenerator"; public string FileNameSuffix => ".Parsable"; - private ParsableCodeGenerator(bool isForValidatableEnum) + private ParsableCodeGenerator( + bool isForEnum, + bool isForValidatableEnum) { + _isForEnum = isForEnum; _isForValidatableEnum = isForValidatableEnum; } @@ -21,24 +26,57 @@ public void GenerateBaseTypes(StringBuilder sb, ParsableGeneratorState state) { sb.Append(@" global::System.IParsable<").AppendTypeFullyQualified(state.Type).Append(">"); + + if (_isForEnum && state.KeyMember?.IsString() == true) + { + sb.Append(@" +#if NET9_0_OR_GREATER + , global::System.ISpanParsable<").AppendTypeFullyQualified(state.Type).Append(@"> +#endif"); + } } public void GenerateImplementation(StringBuilder sb, ParsableGeneratorState state) { + var isKeyTypeString = state.KeyMember?.IsString() == true; + GenerateValidate(sb, state); + GenerateParse(sb, state); + + if (_isForEnum && isKeyTypeString) + GenerateParseForReadOnlySpanOfChar(sb, state); + GenerateTryParse(sb, state); + + if (_isForEnum && isKeyTypeString) + GenerateTryParseForReadOnlySpanOfChar(sb, state); } - private static void GenerateValidate(StringBuilder sb, ParsableGeneratorState state) + private void GenerateValidate(StringBuilder sb, ParsableGeneratorState state) { - var keyType = state.KeyMember?.IsString() == true || state.HasStringBasedValidateMethod ? "string" : state.KeyMember?.TypeFullyQualified; + var isKeyTypeString = state.KeyMember?.IsString() == true; + var keyType = isKeyTypeString || state.HasStringBasedValidateMethod ? "string" : state.KeyMember?.TypeFullyQualified; + sb.Append(@" private static ").AppendTypeFullyQualified(state.ValidationError).Append("? Validate(").Append(keyType).Append(" key, global::System.IFormatProvider? provider, out ").AppendTypeFullyQualifiedNullAnnotated(state.Type).Append(@" result) where T : global::Thinktecture.IValueObjectFactory<").AppendTypeFullyQualified(state.Type).Append(", ").Append(keyType).Append(", ").AppendTypeFullyQualified(state.ValidationError).Append(@"> { return T.Validate(key, provider, out result); }"); + + if (_isForEnum && isKeyTypeString) + { + sb.Append(@" + +#if NET9_0_OR_GREATER + private static ").AppendTypeFullyQualified(state.ValidationError).Append("? Validate(global::System.ReadOnlySpan key, global::System.IFormatProvider? provider, out ").AppendTypeFullyQualifiedNullAnnotated(state.Type).Append(@" result) + where T : global::Thinktecture.IValueObjectFactory<").AppendTypeFullyQualified(state.Type).Append(", global::System.ReadOnlySpan, ").AppendTypeFullyQualified(state.ValidationError).Append(@"> + { + return T.Validate(key, provider, out result); + } +#endif"); + } } private void GenerateParse(StringBuilder sb, ParsableGeneratorState state) @@ -79,6 +117,39 @@ private void GenerateParse(StringBuilder sb, ParsableGeneratorState state) } } + private void GenerateParseForReadOnlySpanOfChar(StringBuilder sb, ParsableGeneratorState state) + { + sb.Append(@" + +#if NET9_0_OR_GREATER + /// + public static ").AppendTypeFullyQualified(state.Type).Append(@" Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider? provider) + {"); + + sb.Append(@" + var validationError = Validate<").AppendTypeFullyQualified(state.Type).Append(">(s, provider, out var result);"); + + if (_isForValidatableEnum) + { + sb.Append(@" + return result!; + }"); + } + else + { + sb.Append(@" + + if(validationError is null) + return result!; + + throw new global::System.FormatException(validationError.ToString() ?? ""Unable to parse \""").Append(state.Type.Name).Append(@"\"".""); + }"); + } + + sb.Append(@" +#endif"); + } + private void GenerateTryParse(StringBuilder sb, ParsableGeneratorState state) { sb.Append(@" @@ -127,4 +198,34 @@ public static bool TryParse( }"); } } + + private void GenerateTryParseForReadOnlySpanOfChar(StringBuilder sb, ParsableGeneratorState state) + { + sb.Append(@" + +#if NET9_0_OR_GREATER + /// + public static bool TryParse( + global::System.ReadOnlySpan s, + global::System.IFormatProvider? provider, + [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out ").AppendTypeFullyQualified(state.Type).Append(@" result) + { + var validationError = Validate<").AppendTypeFullyQualified(state.Type).Append(">(s, provider, out result!);"); + + if (_isForValidatableEnum) + { + sb.Append(@" + return true; + }"); + } + else + { + sb.Append(@" + return validationError is null; + }"); + } + + sb.Append(@" +#endif"); + } } diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ParsableGeneratorState.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ParsableGeneratorState.cs index e89560c1..8909e46d 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ParsableGeneratorState.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ParsableGeneratorState.cs @@ -7,6 +7,7 @@ namespace Thinktecture.CodeAnalysis; public ValidationErrorState ValidationError { get; } public bool SkipIParsable { get; } public bool IsKeyMemberParsable { get; } + public bool IsEnum { get; } public bool IsValidatableEnum { get; } public bool HasStringBasedValidateMethod { get; } @@ -16,6 +17,7 @@ public ParsableGeneratorState( ValidationErrorState validationError, bool skipIParsable, bool isKeyMemberParsable, + bool isEnum, bool isValidatableEnum, bool hasStringBasedValidateMethod) { @@ -24,6 +26,7 @@ public ParsableGeneratorState( ValidationError = validationError; SkipIParsable = skipIParsable; IsKeyMemberParsable = isKeyMemberParsable; + IsEnum = isEnum; IsValidatableEnum = isValidatableEnum; HasStringBasedValidateMethod = hasStringBasedValidateMethod; } @@ -35,6 +38,7 @@ public bool Equals(ParsableGeneratorState other) && ValidationError.Equals(other.ValidationError) && SkipIParsable == other.SkipIParsable && IsKeyMemberParsable == other.IsKeyMemberParsable + && IsEnum == other.IsEnum && IsValidatableEnum == other.IsValidatableEnum && HasStringBasedValidateMethod == other.HasStringBasedValidateMethod; } @@ -53,6 +57,7 @@ public override int GetHashCode() hashCode = (hashCode * 397) ^ ValidationError.GetHashCode(); hashCode = (hashCode * 397) ^ SkipIParsable.GetHashCode(); hashCode = (hashCode * 397) ^ IsKeyMemberParsable.GetHashCode(); + hashCode = (hashCode * 397) ^ IsEnum.GetHashCode(); hashCode = (hashCode * 397) ^ IsValidatableEnum.GetHashCode(); hashCode = (hashCode * 397) ^ HasStringBasedValidateMethod.GetHashCode(); diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumCodeGenerator.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumCodeGenerator.cs index 437c57ca..50ca55ef 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumCodeGenerator.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumCodeGenerator.cs @@ -67,8 +67,18 @@ private void GenerateEnum(CancellationToken cancellationToken) _sb.Append("partial ").Append(_state.IsReferenceType ? "class" : "struct").Append(" ").Append(_state.Name).Append(" :"); if (_state.KeyMember is not null) + { _sb.Append(" global::Thinktecture.IEnum<").AppendTypeFullyQualified(_state.KeyMember).Append(", ").AppendTypeFullyQualified(_state).Append(", ").AppendTypeFullyQualified(_state.ValidationError).Append(">,"); + if (_state.KeyMember.IsString()) + { + _sb.Append(@" +#if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory<").AppendTypeFullyQualified(_state).Append(", global::System.ReadOnlySpan, ").AppendTypeFullyQualified(_state.ValidationError).Append(@">, +#endif"); + } + } + foreach (var desiredFactory in _state.Settings.DesiredFactories) { if (desiredFactory.Equals(_state.KeyMember)) @@ -98,13 +108,51 @@ private void GenerateEnum(CancellationToken cancellationToken) { GenerateModuleInitializer(_state.KeyMember); + if (_state.KeyMember.IsString()) + { + _sb.Append(@" + +#if NET9_0_OR_GREATER + private static readonly global::System.Lazy<"); + + AppendLookupTypeTuple(_state.KeyMember); + _sb.Append(@"> _lookups + = new global::System.Lazy<"); + AppendLookupTypeTuple(_state.KeyMember); + _sb.Append(@">(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static "); + AppendLookupType(_state.KeyMember); + _sb.Append(@" _itemsLookup => _lookups.Value.Item1; +#else"); + } + else + { + _sb.Append(@" +"); + } + _sb.Append(@" + private static readonly global::System.Lazy<"); + AppendLookupType(_state.KeyMember); + _sb.Append(@"> _lookups + = new global::System.Lazy<"); + AppendLookupType(_state.KeyMember); + _sb.Append(@">(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); - private static readonly global::System.Lazy> _itemsLookup - = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static "); + AppendLookupType(_state.KeyMember); + _sb.Append(" _itemsLookup => _lookups.Value;"); + + if (_state.KeyMember.IsString()) + { + _sb.Append(@" +#endif"); + } + _sb.Append(@" private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -157,6 +205,9 @@ private void GenerateEnum(CancellationToken cancellationToken) GenerateToValue(_state.KeyMember); GenerateGet(_state.KeyMember); + if (_state.KeyMember.IsString()) + GenerateGetForReadOnlySpanOfChar(_state.KeyMember); + if (_state.Settings.IsValidatable) GenerateCreateAndCheckInvalidItem(_state.KeyMember, needCreateInvalidItemImplementation); @@ -166,7 +217,15 @@ private void GenerateEnum(CancellationToken cancellationToken) cancellationToken.ThrowIfCancellationRequested(); GenerateTryGet(_state.KeyMember); + + if (_state.KeyMember.IsString()) + GenerateTryGetForReadOnlySpanOfChar(_state.KeyMember); + GenerateValidate(_state.KeyMember); + + if (_state.KeyMember.IsString()) + GenerateValidateForReadOnlySpanOfChar(_state.KeyMember); + GenerateImplicitConversion(_state.KeyMember); GenerateExplicitConversion(_state.KeyMember); } @@ -856,7 +915,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] ") if (_state.Settings.IsValidatable) { _sb.Append(@" - if(_itemsLookup.Value.TryGetValue(").AppendEscaped(keyProperty.ArgumentName).Append(@", out item)) + if(_itemsLookup.TryGetValue(").AppendEscaped(keyProperty.ArgumentName).Append(@", out item)) return true; item = CreateAndCheckInvalidItem(").AppendEscaped(keyProperty.ArgumentName).Append(@"); @@ -865,13 +924,47 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] ") else { _sb.Append(@" - return _itemsLookup.Value.TryGetValue(").AppendEscaped(keyProperty.ArgumentName).Append(", out item);"); + return _itemsLookup.TryGetValue(").AppendEscaped(keyProperty.ArgumentName).Append(", out item);"); } _sb.Append(@" }"); } + private void GenerateTryGetForReadOnlySpanOfChar(IMemberState keyProperty) + { + _sb.Append(@" + +#if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of ").AppendTypeForXmlComment(_state).Append(@". + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan ").AppendEscaped(keyProperty.ArgumentName).Append(", [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out ").AppendTypeFullyQualified(_state).Append(@" item) + {"); + + if (_state.Settings.IsValidatable) + { + _sb.Append(@" + if(_lookups.Value.Item2.TryGetValue(").AppendEscaped(keyProperty.ArgumentName).Append(@", out item)) + return true; + + item = CreateAndCheckInvalidItem(").AppendEscaped(keyProperty.ArgumentName).Append(@".ToString()); + return false;"); + } + else + { + _sb.Append(@" + return _lookups.Value.Item2.TryGetValue(").AppendEscaped(keyProperty.ArgumentName).Append(", out item);"); + } + + _sb.Append(@" + } +#endif"); + } + private void GenerateValidate(IMemberState keyProperty) { var providerArgumentName = keyProperty.ArgumentName == "provider" ? "formatProvider" : "provider"; @@ -915,6 +1008,42 @@ private void GenerateValidate(IMemberState keyProperty) }"); } + private void GenerateValidateForReadOnlySpanOfChar(IMemberState keyProperty) + { + var providerArgumentName = keyProperty.ArgumentName == "provider" ? "formatProvider" : "provider"; + + _sb.Append(@" + +#if NET9_0_OR_GREATER + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of ").AppendTypeForXmlComment(_state).Append(@". + /// null if a valid item with provided exists; ").AppendTypeForXmlComment(_state.ValidationError).Append(@" with an error message otherwise. + public static ").AppendTypeFullyQualified(_state.ValidationError).Append("? Validate(global::System.ReadOnlySpan ").AppendEscaped(keyProperty.ArgumentName).Append(", global::System.IFormatProvider? ").AppendEscaped(providerArgumentName).Append(", [global::System.Diagnostics.CodeAnalysis.MaybeNull] out ").AppendTypeFullyQualified(_state).Append(@" item) + { + if(").AppendTypeFullyQualified(_state).Append(".TryGet(").AppendEscaped(keyProperty.ArgumentName).Append(@", out item)) + { + return null; + } + else + {"); + + if (_state.Settings.IsValidatable) + { + _sb.Append(@" + item = CreateAndCheckInvalidItem(").AppendEscaped(keyProperty.ArgumentName).Append(".ToString());"); + } + + _sb.Append(@" + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError<").AppendTypeFullyQualified(_state.ValidationError).Append(@">($""There is no item of type '").AppendTypeMinimallyQualified(_state).Append("' with the identifier '{").AppendEscaped(keyProperty.ArgumentName).Append(@"}'.""); + } + } +#endif"); + } + private void GenerateImplicitConversion(KeyMemberState keyProperty) { if (keyProperty.IsInterface) @@ -1030,7 +1159,29 @@ private void GenerateGetLookup(KeyMemberState keyMember) _sb.Append(@" - private static global::System.Collections.Generic.IReadOnlyDictionary<").AppendTypeFullyQualified(keyMember).Append(", ").AppendTypeFullyQualified(_state).Append(@"> GetLookup() + private static"); + + if (keyMember.IsString()) + { + _sb.Append(@" +#if NET9_0_OR_GREATER + "); + AppendLookupTypeTuple(keyMember); + _sb.Append(@" +#else + "); + AppendLookupType(keyMember); + _sb.Append(@" +#endif + "); + } + else + { + _sb.Append(" "); + AppendLookupType(keyMember); + } + + _sb.Append(@" GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary<").AppendTypeFullyQualified(keyMember).Append(", ").AppendTypeFullyQualified(_state).Append(">(").Append(totalNumberOfItems); @@ -1096,7 +1247,7 @@ void AddItem(").AppendTypeFullyQualified(_state).Append(@" item, string itemName _sb.Append(@" #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup"); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup"); if (_state.Settings.KeyMemberEqualityComparerAccessor is not null) { @@ -1107,13 +1258,40 @@ void AddItem(").AppendTypeFullyQualified(_state).Append(@" item, string itemName _sb.Append(", global::System.StringComparer.OrdinalIgnoreCase"); } - _sb.Append(@"); + _sb.Append(");"); + + if (keyMember.IsString()) + { + _sb.Append(@" +#if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); +#else + return frozenDictionary; +#endif"); + } + else + { + _sb.Append(@" + return frozenDictionary;"); + } + + _sb.Append(@" #else return lookup; #endif }"); } + private void AppendLookupType(KeyMemberState keyMember) + { + _sb.Append("global::System.Collections.Generic.IReadOnlyDictionary<").AppendTypeFullyQualified(keyMember).Append(", ").AppendTypeFullyQualified(_state).Append(">"); + } + + private void AppendLookupTypeTuple(KeyMemberState keyMember) + { + _sb.Append("(global::System.Collections.Generic.IReadOnlyDictionary<").AppendTypeFullyQualified(keyMember).Append(", ").AppendTypeFullyQualified(_state).Append(">, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>").Append(")"); + } + private void GenerateGetItems() { var totalNumberOfItems = _state.Items.Count; @@ -1218,7 +1396,7 @@ private void GenerateGet(IMemberState keyProperty) } _sb.Append(@" - if (!_itemsLookup.Value.TryGetValue(").AppendEscaped(keyProperty.ArgumentName).Append(@", out var item)) + if (!_itemsLookup.TryGetValue(").AppendEscaped(keyProperty.ArgumentName).Append(@", out var item)) {"); if (_state.Settings.IsValidatable) @@ -1239,6 +1417,48 @@ private void GenerateGet(IMemberState keyProperty) }"); } + private void GenerateGetForReadOnlySpanOfChar(IMemberState keyProperty) + { + _sb.Append(@" + +#if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of ").AppendTypeForXmlComment(_state).Append(@" if is not null; otherwise null."); + + if (!_state.Settings.IsValidatable) + { + _sb.Append(@" + /// If there is no item with the provided ."); + } + + _sb.Append(@" + public static ").AppendTypeFullyQualified(_state).Append(" ").Append(Constants.Methods.GET).Append("(global::System.ReadOnlySpan ").AppendEscaped(keyProperty.ArgumentName).Append(@") + { + if (!_lookups.Value.Item2.TryGetValue(").AppendEscaped(keyProperty.ArgumentName).Append(@", out var item)) + {"); + + if (_state.Settings.IsValidatable) + { + _sb.Append(@" + item = CreateAndCheckInvalidItem(").AppendEscaped(keyProperty.ArgumentName).Append(".ToString());"); + } + else + { + _sb.Append(@" + throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(").AppendTypeFullyQualified(_state).Append("), ").AppendEscaped(keyProperty.ArgumentName).Append(".ToString());"); + } + + _sb.Append(@" + } + + return item; + } +#endif"); + } + private void GenerateCreateAndCheckInvalidItem(IMemberState keyProperty, bool needsCreateInvalidItemImplementation) { _sb.Append(@" @@ -1275,7 +1495,7 @@ private void GenerateCreateAndCheckInvalidItem(IMemberState keyProperty, bool ne { _sb.Append(@" - if (_itemsLookup.Value.ContainsKey(item.").Append(keyProperty.Name).Append(@")) + if (_itemsLookup.ContainsKey(item.").Append(keyProperty.Name).Append(@")) throw new global::System.Exception(""The implementation of method '").Append(Constants.Methods.CREATE_INVALID_ITEM).Append("' must not return an instance with property '").Append(keyProperty.Name).Append(@"' equals to one of a valid item."");"); } diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumSourceGenerator.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumSourceGenerator.cs index f1dbbdc0..66471963 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumSourceGenerator.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumSourceGenerator.cs @@ -108,6 +108,7 @@ private void InitializeParsableCodeGenerator(IncrementalGeneratorInitializationC state.State.ValidationError, state.Settings.SkipIParsable, state.KeyMember.IsParsable, + true, state.State.Settings.IsValidatable, state.AttributeInfo.DesiredFactories.Any(t => t.SpecialType == SpecialType.System_String))); }); diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ThinktectureSourceGeneratorBase.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ThinktectureSourceGeneratorBase.cs index 71dafc7e..f0d70a60 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ThinktectureSourceGeneratorBase.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ThinktectureSourceGeneratorBase.cs @@ -205,7 +205,7 @@ protected void InitializeParsableCodeGenerator( state.Left.Type.Name, state.Left, state.Right, - InterfaceCodeGeneratorFactory.Parsable(state.Left.IsValidatableEnum))); + InterfaceCodeGeneratorFactory.Parsable(state.Left.IsEnum, state.Left.IsValidatableEnum))); } protected void InitializeComparisonOperatorsCodeGenerator( diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ValueObjects/ValueObjectSourceGenerator.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ValueObjects/ValueObjectSourceGenerator.cs index c35e0f1d..4d1e0af6 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ValueObjects/ValueObjectSourceGenerator.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ValueObjects/ValueObjectSourceGenerator.cs @@ -233,6 +233,7 @@ private void InitializeParsableCodeGenerator(IncrementalGeneratorInitializationC state.Settings.SkipIParsable, state.State.KeyMember.IsParsable, false, + false, state.AttributeInfo.DesiredFactories.Any(t => t.SpecialType == SpecialType.System_String))); InitializeParsableCodeGenerator(context, parsables, options); @@ -247,6 +248,7 @@ private void InitializeParsableCodeGenerator(IncrementalGeneratorInitializationC state.Settings.SkipIParsable, false, false, + false, state.AttributeInfo.DesiredFactories.Any(t => t.SpecialType == SpecialType.System_String))); InitializeParsableCodeGenerator(context, parsables, options); diff --git a/src/Thinktecture.Runtime.Extensions/IValueObjectFactory.cs b/src/Thinktecture.Runtime.Extensions/IValueObjectFactory.cs index e4368124..ed17dc5b 100644 --- a/src/Thinktecture.Runtime.Extensions/IValueObjectFactory.cs +++ b/src/Thinktecture.Runtime.Extensions/IValueObjectFactory.cs @@ -9,6 +9,9 @@ namespace Thinktecture; // ReSharper disable once UnusedTypeParameter public interface IValueObjectFactory where TValue : notnull +#if NET9_0_OR_GREATER + , allows ref struct +#endif // ReSharper disable once RedundantTypeDeclarationBody { } @@ -24,6 +27,9 @@ public interface IValueObjectFactory /// public interface IValueObjectFactory : IValueObjectFactory where TValue : notnull +#if NET9_0_OR_GREATER + , allows ref struct +#endif where TValidationError : class, IValidationError { /// diff --git a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/EnumSourceGeneratorTests.cs b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/EnumSourceGeneratorTests.cs index 33144d0b..81b6dc2d 100644 --- a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/EnumSourceGeneratorTests.cs +++ b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/EnumSourceGeneratorTests.cs @@ -61,10 +61,419 @@ public EnumSourceGeneratorTests(ITestOutputHelper output) private const string _MAIN_OUTPUT_STRING_BASED_ABSTRACT_CLASS = _GENERATED_HEADER + """ + namespace Thinktecture.Tests + { + [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] + partial class TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError>, + #endif + global::System.IEquatable + { + [global::System.Runtime.CompilerServices.ModuleInitializer] + internal static void ModuleInit() + { + var convertFromKey = new global::System.Func(global::Thinktecture.Tests.TestEnum.Get); + global::System.Linq.Expressions.Expression> convertFromKeyExpression = static @key => global::Thinktecture.Tests.TestEnum.Get(@key); + + var convertToKey = new global::System.Func(static item => item.Key); + global::System.Linq.Expressions.Expression> convertToKeyExpression = static item => item.Key; + + var enumType = typeof(global::Thinktecture.Tests.TestEnum); + var metadata = new global::Thinktecture.Internal.KeyedValueObjectMetadata(enumType, typeof(string), true, false, convertFromKey, convertFromKeyExpression, null, convertToKey, convertToKeyExpression); + + global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); + } + + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups + = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif + private static readonly global::System.Lazy> _items + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + /// + /// Gets all valid items. + /// + public static global::System.Collections.Generic.IReadOnlyList Items => _items.Value; + + /// + /// The identifier of this item. + /// + public string Key { get; } + + private readonly int _hashCode; + private readonly global::System.Lazy _itemIndex; + + private TestEnum( + string @key) + { + ValidateConstructorArguments( + ref @key); + + if (@key is null) + throw new global::System.ArgumentNullException(nameof(@key)); + + this.Key = @key; + this._hashCode = global::System.HashCode.Combine(typeof(global::Thinktecture.Tests.TestEnum), global::System.StringComparer.OrdinalIgnoreCase.GetHashCode(@key)); + this._itemIndex = new global::System.Lazy(() => + { + for (var i = 0; i < Items.Count; i++) + { + if (this == Items[i]) + return i; + } + + throw new global::System.Exception($"Current item '{@key}' not found in 'Items'."); + }, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + } + + static partial void ValidateConstructorArguments( + ref string @key); + + /// + /// Gets the identifier of the item. + /// + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + string global::Thinktecture.IValueObjectConvertable.ToValue() + { + return this.Key; + } + + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + /// If there is no item with the provided . + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("key")] + public static global::Thinktecture.Tests.TestEnum? Get(string? @key) + { + if (@key is null) + return default; + + if (!_itemsLookup.TryGetValue(@key, out var item)) + { + throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); + } + + return item; + } + + #if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + /// If there is no item with the provided . + public static global::Thinktecture.Tests.TestEnum Get(global::System.ReadOnlySpan @key) + { + if (!_lookups.Value.Item2.TryGetValue(@key, out var item)) + { + throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key.ToString()); + } + + return item; + } + #endif + + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + { + if (@key is null) + { + item = default; + return false; + } + + return _itemsLookup.TryGetValue(@key, out item); + } + + #if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + { + return _lookups.Value.Item2.TryGetValue(@key, out item); + } + #endif + + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + { + if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) + { + return null; + } + else + { + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); + } + } + + #if NET9_0_OR_GREATER + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + { + if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) + { + return null; + } + else + { + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); + } + } + #endif + + /// + /// Implicit conversion to the type . + /// + /// Item to covert. + /// The of provided or default if is null. + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("item")] + public static implicit operator string?(global::Thinktecture.Tests.TestEnum? item) + { + return item is null ? default : item.Key; + } + + /// + /// Explicit conversion from the type . + /// + /// Value to covert. + /// An instance of if the is a known item or implements . + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("key")] + public static explicit operator global::Thinktecture.Tests.TestEnum?(string? @key) + { + return global::Thinktecture.Tests.TestEnum.Get(@key); + } + + /// + public bool Equals(global::Thinktecture.Tests.TestEnum? other) + { + return global::System.Object.ReferenceEquals(this, other); + } + + /// + public override bool Equals(object? other) + { + return other is global::Thinktecture.Tests.TestEnum item && Equals(item); + } + + /// + public override int GetHashCode() + { + return _hashCode; + } + + /// + public override string ToString() + { + return this.Key.ToString(); + } + + /// + /// Executes an action depending on the current item. + /// + /// The action to execute if the current item is equal to . + /// The action to execute if the current item is equal to . + public void Switch( + global::System.Action @item1, + global::System.Action @item2) + { + switch (_itemIndex.Value) + { + case 0: + @item1(); + return; + case 1: + @item2(); + return; + default: + throw new global::System.ArgumentOutOfRangeException($"Unknown item '{this}'."); + } + } + + /// + /// Executes an action depending on the current item. + /// + /// State to be passed to the callbacks. + /// The action to execute if the current item is equal to . + /// The action to execute if the current item is equal to . + public void Switch( + TState state, + global::System.Action @item1, + global::System.Action @item2) + #if NET9_0_OR_GREATER + where TState : allows ref struct + #endif + { + switch (_itemIndex.Value) + { + case 0: + @item1(state); + return; + case 1: + @item2(state); + return; + default: + throw new global::System.ArgumentOutOfRangeException($"Unknown item '{this}'."); + } + } + + /// + /// Executes a function depending on the current item. + /// + /// The function to execute if the current item is equal to . + /// The function to execute if the current item is equal to . + public TResult Switch( + global::System.Func @item1, + global::System.Func @item2) + #if NET9_0_OR_GREATER + where TResult : allows ref struct + #endif + { + switch (_itemIndex.Value) + { + case 0: + return @item1(); + case 1: + return @item2(); + default: + throw new global::System.ArgumentOutOfRangeException($"Unknown item '{this}'."); + } + } + + /// + /// Executes a function depending on the current item. + /// + /// State to be passed to the callbacks. + /// The function to execute if the current item is equal to . + /// The function to execute if the current item is equal to . + public TResult Switch( + TState state, + global::System.Func @item1, + global::System.Func @item2) + #if NET9_0_OR_GREATER + where TResult : allows ref struct + where TState : allows ref struct + #endif + { + switch (_itemIndex.Value) + { + case 0: + return @item1(state); + case 1: + return @item2(state); + default: + throw new global::System.ArgumentOutOfRangeException($"Unknown item '{this}'."); + } + } + + /// + /// Maps an item to an instance of type . + /// + /// The instance to return if the current item is equal to . + /// The instance to return if the current item is equal to . + public TResult Map( + TResult @item1, + TResult @item2) + #if NET9_0_OR_GREATER + where TResult : allows ref struct + #endif + { + switch (_itemIndex.Value) + { + case 0: + return @item1; + case 1: + return @item2; + default: + throw new global::System.ArgumentOutOfRangeException($"Unknown item '{this}'."); + } + } + + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() + { + var lookup = new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.OrdinalIgnoreCase); + + void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) + { + if (item is null) + throw new global::System.ArgumentNullException($"The item \"{itemName}\" of type \"TestEnum\" must not be null."); + + if (item.Key is null) + throw new global::System.ArgumentException($"The \"Key\" of the item \"{itemName}\" of type \"TestEnum\" must not be null."); + + if (lookup.ContainsKey(item.Key)) + throw new global::System.ArgumentException($"The type \"TestEnum\" has multiple items with the identifier \"{item.Key}\"."); + + lookup.Add(item.Key, item); + } + + AddItem(@Item1, nameof(@Item1)); + AddItem(@Item2, nameof(@Item2)); + + #if NET8_0_OR_GREATER + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif + #else + return lookup; + #endif + } + } + } + + """; + + private const string _MAIN_OUTPUT_STRING_BASED_CLASS = _GENERATED_HEADER + """ + namespace Thinktecture.Tests { [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] - partial class TestEnum : global::Thinktecture.IEnum, + sealed partial class TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError>, + #endif global::System.IEquatable { [global::System.Runtime.CompilerServices.ModuleInitializer] @@ -82,11 +491,19 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -148,7 +565,7 @@ static partial void ValidateConstructorArguments( if (@key is null) return default; - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); } @@ -156,355 +573,74 @@ static partial void ValidateConstructorArguments( return item; } + #if NET9_0_OR_GREATER /// - /// Gets a valid enumeration item for provided if a valid item exists. + /// Gets an enumeration item for provided . /// /// The identifier to return an enumeration item for. - /// An instance of . - /// true if a valid item with provided exists; false otherwise. - public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + /// An instance of if is not null; otherwise null. + /// If there is no item with the provided . + public static global::Thinktecture.Tests.TestEnum Get(global::System.ReadOnlySpan @key) { - if (@key is null) + if (!_lookups.Value.Item2.TryGetValue(@key, out var item)) { - item = default; - return false; + throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key.ToString()); } - return _itemsLookup.Value.TryGetValue(@key, out item); + return item; } + #endif /// - /// Validates the provided and returns a valid enumeration item if found. + /// Gets a valid enumeration item for provided if a valid item exists. /// /// The identifier to return an enumeration item for. - /// An object that provides culture-specific formatting information. /// An instance of . - /// null if a valid item with provided exists; with an error message otherwise. - public static global::Thinktecture.ValidationError? Validate([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) - { - if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) - { - return null; - } - else - { - return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); - } - } - - /// - /// Implicit conversion to the type . - /// - /// Item to covert. - /// The of provided or default if is null. - [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("item")] - public static implicit operator string?(global::Thinktecture.Tests.TestEnum? item) - { - return item is null ? default : item.Key; - } - - /// - /// Explicit conversion from the type . - /// - /// Value to covert. - /// An instance of if the is a known item or implements . - [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("key")] - public static explicit operator global::Thinktecture.Tests.TestEnum?(string? @key) - { - return global::Thinktecture.Tests.TestEnum.Get(@key); - } - - /// - public bool Equals(global::Thinktecture.Tests.TestEnum? other) - { - return global::System.Object.ReferenceEquals(this, other); - } - - /// - public override bool Equals(object? other) - { - return other is global::Thinktecture.Tests.TestEnum item && Equals(item); - } - - /// - public override int GetHashCode() - { - return _hashCode; - } - - /// - public override string ToString() - { - return this.Key.ToString(); - } - - /// - /// Executes an action depending on the current item. - /// - /// The action to execute if the current item is equal to . - /// The action to execute if the current item is equal to . - public void Switch( - global::System.Action @item1, - global::System.Action @item2) + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) { - switch (_itemIndex.Value) + if (@key is null) { - case 0: - @item1(); - return; - case 1: - @item2(); - return; - default: - throw new global::System.ArgumentOutOfRangeException($"Unknown item '{this}'."); + item = default; + return false; } - } - /// - /// Executes an action depending on the current item. - /// - /// State to be passed to the callbacks. - /// The action to execute if the current item is equal to . - /// The action to execute if the current item is equal to . - public void Switch( - TState state, - global::System.Action @item1, - global::System.Action @item2) - #if NET9_0_OR_GREATER - where TState : allows ref struct - #endif - { - switch (_itemIndex.Value) - { - case 0: - @item1(state); - return; - case 1: - @item2(state); - return; - default: - throw new global::System.ArgumentOutOfRangeException($"Unknown item '{this}'."); - } + return _itemsLookup.TryGetValue(@key, out item); } - /// - /// Executes a function depending on the current item. - /// - /// The function to execute if the current item is equal to . - /// The function to execute if the current item is equal to . - public TResult Switch( - global::System.Func @item1, - global::System.Func @item2) #if NET9_0_OR_GREATER - where TResult : allows ref struct - #endif - { - switch (_itemIndex.Value) - { - case 0: - return @item1(); - case 1: - return @item2(); - default: - throw new global::System.ArgumentOutOfRangeException($"Unknown item '{this}'."); - } - } - - /// - /// Executes a function depending on the current item. - /// - /// State to be passed to the callbacks. - /// The function to execute if the current item is equal to . - /// The function to execute if the current item is equal to . - public TResult Switch( - TState state, - global::System.Func @item1, - global::System.Func @item2) - #if NET9_0_OR_GREATER - where TResult : allows ref struct - where TState : allows ref struct - #endif - { - switch (_itemIndex.Value) - { - case 0: - return @item1(state); - case 1: - return @item2(state); - default: - throw new global::System.ArgumentOutOfRangeException($"Unknown item '{this}'."); - } - } - /// - /// Maps an item to an instance of type . + /// Gets a valid enumeration item for provided if a valid item exists. /// - /// The instance to return if the current item is equal to . - /// The instance to return if the current item is equal to . - public TResult Map( - TResult @item1, - TResult @item2) - #if NET9_0_OR_GREATER - where TResult : allows ref struct - #endif + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) { - switch (_itemIndex.Value) - { - case 0: - return @item1; - case 1: - return @item2; - default: - throw new global::System.ArgumentOutOfRangeException($"Unknown item '{this}'."); - } + return _lookups.Value.Item2.TryGetValue(@key, out item); } - - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() - { - var lookup = new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.OrdinalIgnoreCase); - - void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) - { - if (item is null) - throw new global::System.ArgumentNullException($"The item \"{itemName}\" of type \"TestEnum\" must not be null."); - - if (item.Key is null) - throw new global::System.ArgumentException($"The \"Key\" of the item \"{itemName}\" of type \"TestEnum\" must not be null."); - - if (lookup.ContainsKey(item.Key)) - throw new global::System.ArgumentException($"The type \"TestEnum\" has multiple items with the identifier \"{item.Key}\"."); - - lookup.Add(item.Key, item); - } - - AddItem(@Item1, nameof(@Item1)); - AddItem(@Item2, nameof(@Item2)); - - #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); - #else - return lookup; #endif - } - } - } - - """; - - private const string _MAIN_OUTPUT_STRING_BASED_CLASS = _GENERATED_HEADER + """ - - namespace Thinktecture.Tests - { - [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] - sealed partial class TestEnum : global::Thinktecture.IEnum, - global::System.IEquatable - { - [global::System.Runtime.CompilerServices.ModuleInitializer] - internal static void ModuleInit() - { - var convertFromKey = new global::System.Func(global::Thinktecture.Tests.TestEnum.Get); - global::System.Linq.Expressions.Expression> convertFromKeyExpression = static @key => global::Thinktecture.Tests.TestEnum.Get(@key); - - var convertToKey = new global::System.Func(static item => item.Key); - global::System.Linq.Expressions.Expression> convertToKeyExpression = static item => item.Key; - - var enumType = typeof(global::Thinktecture.Tests.TestEnum); - var metadata = new global::Thinktecture.Internal.KeyedValueObjectMetadata(enumType, typeof(string), true, false, convertFromKey, convertFromKeyExpression, null, convertToKey, convertToKeyExpression); - - global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); - } - - private static readonly global::System.Lazy> _itemsLookup - = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); - - private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); - - /// - /// Gets all valid items. - /// - public static global::System.Collections.Generic.IReadOnlyList Items => _items.Value; - - /// - /// The identifier of this item. - /// - public string Key { get; } - - private readonly int _hashCode; - private readonly global::System.Lazy _itemIndex; - - private TestEnum( - string @key) - { - ValidateConstructorArguments( - ref @key); - - if (@key is null) - throw new global::System.ArgumentNullException(nameof(@key)); - - this.Key = @key; - this._hashCode = global::System.HashCode.Combine(typeof(global::Thinktecture.Tests.TestEnum), global::System.StringComparer.OrdinalIgnoreCase.GetHashCode(@key)); - this._itemIndex = new global::System.Lazy(() => - { - for (var i = 0; i < Items.Count; i++) - { - if (this == Items[i]) - return i; - } - - throw new global::System.Exception($"Current item '{@key}' not found in 'Items'."); - }, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); - } - - static partial void ValidateConstructorArguments( - ref string @key); /// - /// Gets the identifier of the item. - /// - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - string global::Thinktecture.IValueObjectConvertable.ToValue() - { - return this.Key; - } - - /// - /// Gets an enumeration item for provided . + /// Validates the provided and returns a valid enumeration item if found. /// /// The identifier to return an enumeration item for. - /// An instance of if is not null; otherwise null. - /// If there is no item with the provided . - [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("key")] - public static global::Thinktecture.Tests.TestEnum? Get(string? @key) + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) { - if (@key is null) - return default; - - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) { - throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); + return null; } - - return item; - } - - /// - /// Gets a valid enumeration item for provided if a valid item exists. - /// - /// The identifier to return an enumeration item for. - /// An instance of . - /// true if a valid item with provided exists; false otherwise. - public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) - { - if (@key is null) + else { - item = default; - return false; + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); } - - return _itemsLookup.Value.TryGetValue(@key, out item); } + #if NET9_0_OR_GREATER /// /// Validates the provided and returns a valid enumeration item if found. /// @@ -512,7 +648,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st /// An object that provides culture-specific formatting information. /// An instance of . /// null if a valid item with provided exists; with an error message otherwise. - public static global::Thinktecture.ValidationError? Validate([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + public static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) { if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) { @@ -523,6 +659,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); } } + #endif /// /// Implicit conversion to the type . @@ -691,7 +828,13 @@ public TResult Map( } } - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.OrdinalIgnoreCase); @@ -713,7 +856,12 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif #else return lookup; #endif @@ -746,11 +894,12 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -805,7 +954,7 @@ static partial void ValidateConstructorArguments( /// If there is no item with the provided . public static global::Thinktecture.Tests.TestEnum Get(int @key) { - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); } @@ -821,7 +970,7 @@ static partial void ValidateConstructorArguments( /// true if a valid item with provided exists; false otherwise. public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] int @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) { - return _itemsLookup.Value.TryGetValue(@key, out item); + return _itemsLookup.TryGetValue(@key, out item); } /// @@ -1029,7 +1178,8 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup); + return frozenDictionary; #else return lookup; #endif @@ -1109,6 +1259,9 @@ namespace Thinktecture.Tests; partial class TestEnum : global::System.IParsable + #if NET9_0_OR_GREATER + , global::System.ISpanParsable + #endif { private static global::Thinktecture.ValidationError? Validate(string key, global::System.IFormatProvider? provider, out global::Thinktecture.Tests.TestEnum? result) where T : global::Thinktecture.IValueObjectFactory @@ -1116,6 +1269,14 @@ partial class TestEnum : return T.Validate(key, provider, out result); } + #if NET9_0_OR_GREATER + private static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan key, global::System.IFormatProvider? provider, out global::Thinktecture.Tests.TestEnum? result) + where T : global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError> + { + return T.Validate(key, provider, out result); + } + #endif + /// public static global::Thinktecture.Tests.TestEnum Parse(string s, global::System.IFormatProvider? provider) { @@ -1127,6 +1288,19 @@ partial class TestEnum : throw new global::System.FormatException(validationError.ToString() ?? "Unable to parse \"TestEnum\"."); } + #if NET9_0_OR_GREATER + /// + public static global::Thinktecture.Tests.TestEnum Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider? provider) + { + var validationError = Validate(s, provider, out var result); + + if(validationError is null) + return result!; + + throw new global::System.FormatException(validationError.ToString() ?? "Unable to parse \"TestEnum\"."); + } + #endif + /// public static bool TryParse( string? s, @@ -1142,6 +1316,18 @@ public static bool TryParse( var validationError = Validate(s, provider, out result!); return validationError is null; } + + #if NET9_0_OR_GREATER + /// + public static bool TryParse( + global::System.ReadOnlySpan s, + global::System.IFormatProvider? provider, + [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum result) + { + var validationError = Validate(s, provider, out result!); + return validationError is null; + } + #endif } """; @@ -1336,6 +1522,9 @@ namespace Thinktecture.Tests; partial class TestEnum : global::System.IParsable + #if NET9_0_OR_GREATER + , global::System.ISpanParsable + #endif { private static global::Thinktecture.ValidationError? Validate(string key, global::System.IFormatProvider? provider, out global::Thinktecture.Tests.TestEnum? result) where T : global::Thinktecture.IValueObjectFactory @@ -1343,12 +1532,29 @@ partial class TestEnum : return T.Validate(key, provider, out result); } + #if NET9_0_OR_GREATER + private static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan key, global::System.IFormatProvider? provider, out global::Thinktecture.Tests.TestEnum? result) + where T : global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError> + { + return T.Validate(key, provider, out result); + } + #endif + + /// + public static global::Thinktecture.Tests.TestEnum Parse(string s, global::System.IFormatProvider? provider) + { + var validationError = Validate(s, provider, out var result); + return result!; + } + + #if NET9_0_OR_GREATER /// - public static global::Thinktecture.Tests.TestEnum Parse(string s, global::System.IFormatProvider? provider) + public static global::Thinktecture.Tests.TestEnum Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider? provider) { var validationError = Validate(s, provider, out var result); return result!; } + #endif /// public static bool TryParse( @@ -1365,6 +1571,18 @@ public static bool TryParse( var validationError = Validate(s, provider, out result!); return true; } + + #if NET9_0_OR_GREATER + /// + public static bool TryParse( + global::System.ReadOnlySpan s, + global::System.IFormatProvider? provider, + [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum result) + { + var validationError = Validate(s, provider, out result!); + return true; + } + #endif } """; @@ -2227,6 +2445,9 @@ namespace Thinktecture.Tests { [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] sealed partial class TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.Tests.TestEnumValidationError>, + #endif global::System.IEquatable { [global::System.Runtime.CompilerServices.ModuleInitializer] @@ -2244,11 +2465,19 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -2310,7 +2539,7 @@ static partial void ValidateConstructorArguments( if (@key is null) return default; - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); } @@ -2318,6 +2547,24 @@ static partial void ValidateConstructorArguments( return item; } + #if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + /// If there is no item with the provided . + public static global::Thinktecture.Tests.TestEnum Get(global::System.ReadOnlySpan @key) + { + if (!_lookups.Value.Item2.TryGetValue(@key, out var item)) + { + throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key.ToString()); + } + + return item; + } + #endif + /// /// Gets a valid enumeration item for provided if a valid item exists. /// @@ -2332,9 +2579,22 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st return false; } - return _itemsLookup.Value.TryGetValue(@key, out item); + return _itemsLookup.TryGetValue(@key, out item); } + #if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + { + return _lookups.Value.Item2.TryGetValue(@key, out item); + } + #endif + /// /// Validates the provided and returns a valid enumeration item if found. /// @@ -2354,6 +2614,27 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st } } + #if NET9_0_OR_GREATER + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.Tests.TestEnumValidationError? Validate(global::System.ReadOnlySpan @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + { + if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) + { + return null; + } + else + { + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); + } + } + #endif + /// /// Implicit conversion to the type . /// @@ -2692,7 +2973,13 @@ public TResult MapPartially( return @default; } - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.OrdinalIgnoreCase); @@ -2714,7 +3001,12 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif #else return lookup; #endif @@ -2730,6 +3022,9 @@ namespace Thinktecture.Tests; partial class TestEnum : global::System.IParsable + #if NET9_0_OR_GREATER + , global::System.ISpanParsable + #endif { private static global::Thinktecture.Tests.TestEnumValidationError? Validate(string key, global::System.IFormatProvider? provider, out global::Thinktecture.Tests.TestEnum? result) where T : global::Thinktecture.IValueObjectFactory @@ -2737,6 +3032,14 @@ partial class TestEnum : return T.Validate(key, provider, out result); } + #if NET9_0_OR_GREATER + private static global::Thinktecture.Tests.TestEnumValidationError? Validate(global::System.ReadOnlySpan key, global::System.IFormatProvider? provider, out global::Thinktecture.Tests.TestEnum? result) + where T : global::Thinktecture.IValueObjectFactory, global::Thinktecture.Tests.TestEnumValidationError> + { + return T.Validate(key, provider, out result); + } + #endif + /// public static global::Thinktecture.Tests.TestEnum Parse(string s, global::System.IFormatProvider? provider) { @@ -2748,6 +3051,19 @@ partial class TestEnum : throw new global::System.FormatException(validationError.ToString() ?? "Unable to parse \"TestEnum\"."); } + #if NET9_0_OR_GREATER + /// + public static global::Thinktecture.Tests.TestEnum Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider? provider) + { + var validationError = Validate(s, provider, out var result); + + if(validationError is null) + return result!; + + throw new global::System.FormatException(validationError.ToString() ?? "Unable to parse \"TestEnum\"."); + } + #endif + /// public static bool TryParse( string? s, @@ -2763,6 +3079,18 @@ public static bool TryParse( var validationError = Validate(s, provider, out result!); return validationError is null; } + + #if NET9_0_OR_GREATER + /// + public static bool TryParse( + global::System.ReadOnlySpan s, + global::System.IFormatProvider? provider, + [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum result) + { + var validationError = Validate(s, provider, out result!); + return validationError is null; + } + #endif } """); @@ -2806,6 +3134,9 @@ namespace Thinktecture.Tests { [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] sealed partial class TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError>, + #endif global::System.IEquatable { [global::System.Runtime.CompilerServices.ModuleInitializer] @@ -2823,11 +3154,19 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -2889,7 +3228,7 @@ static partial void ValidateConstructorArguments( if (@key is null) return default; - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); } @@ -2897,6 +3236,24 @@ static partial void ValidateConstructorArguments( return item; } + #if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + /// If there is no item with the provided . + public static global::Thinktecture.Tests.TestEnum Get(global::System.ReadOnlySpan @key) + { + if (!_lookups.Value.Item2.TryGetValue(@key, out var item)) + { + throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key.ToString()); + } + + return item; + } + #endif + /// /// Gets a valid enumeration item for provided if a valid item exists. /// @@ -2911,8 +3268,21 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st return false; } - return _itemsLookup.Value.TryGetValue(@key, out item); + return _itemsLookup.TryGetValue(@key, out item); + } + + #if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + { + return _lookups.Value.Item2.TryGetValue(@key, out item); } + #endif /// /// Validates the provided and returns a valid enumeration item if found. @@ -2933,6 +3303,27 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st } } + #if NET9_0_OR_GREATER + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + { + if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) + { + return null; + } + else + { + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); + } + } + #endif + /// /// Implicit conversion to the type . /// @@ -3035,7 +3426,13 @@ public TResult MapPartially( return @default; } - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.OrdinalIgnoreCase); @@ -3057,7 +3454,12 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif #else return lookup; #endif @@ -3106,6 +3508,9 @@ namespace Thinktecture.Tests { [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] sealed partial class TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError>, + #endif global::System.IEquatable { [global::System.Runtime.CompilerServices.ModuleInitializer] @@ -3123,11 +3528,19 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -3189,7 +3602,7 @@ static partial void ValidateConstructorArguments( if (@key is null) return default; - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); } @@ -3197,6 +3610,24 @@ static partial void ValidateConstructorArguments( return item; } + #if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + /// If there is no item with the provided . + public static global::Thinktecture.Tests.TestEnum Get(global::System.ReadOnlySpan @key) + { + if (!_lookups.Value.Item2.TryGetValue(@key, out var item)) + { + throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key.ToString()); + } + + return item; + } + #endif + /// /// Gets a valid enumeration item for provided if a valid item exists. /// @@ -3211,8 +3642,21 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st return false; } - return _itemsLookup.Value.TryGetValue(@key, out item); + return _itemsLookup.TryGetValue(@key, out item); + } + + #if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + { + return _lookups.Value.Item2.TryGetValue(@key, out item); } + #endif /// /// Validates the provided and returns a valid enumeration item if found. @@ -3233,6 +3677,27 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st } } + #if NET9_0_OR_GREATER + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + { + if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) + { + return null; + } + else + { + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); + } + } + #endif + /// /// Implicit conversion to the type . /// @@ -3515,7 +3980,13 @@ public TResult SwitchPartially( return @default(state, this); } - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.OrdinalIgnoreCase); @@ -3537,7 +4008,12 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif #else return lookup; #endif @@ -3596,6 +4072,9 @@ namespace Thinktecture.Tests { [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] sealed partial class TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError>, + #endif global::System.IEquatable { [global::System.Runtime.CompilerServices.ModuleInitializer] @@ -3613,11 +4092,19 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -3713,7 +4200,7 @@ static partial void ValidateConstructorArguments( if (@key is null) return default; - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); } @@ -3721,6 +4208,24 @@ static partial void ValidateConstructorArguments( return item; } + #if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + /// If there is no item with the provided . + public static global::Thinktecture.Tests.TestEnum Get(global::System.ReadOnlySpan @key) + { + if (!_lookups.Value.Item2.TryGetValue(@key, out var item)) + { + throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key.ToString()); + } + + return item; + } + #endif + /// /// Gets a valid enumeration item for provided if a valid item exists. /// @@ -3731,13 +4236,46 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st { if (@key is null) { - item = default; - return false; + item = default; + return false; + } + + return _itemsLookup.TryGetValue(@key, out item); + } + + #if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + { + return _lookups.Value.Item2.TryGetValue(@key, out item); + } + #endif + + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + { + if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) + { + return null; + } + else + { + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); } - - return _itemsLookup.Value.TryGetValue(@key, out item); } + #if NET9_0_OR_GREATER /// /// Validates the provided and returns a valid enumeration item if found. /// @@ -3745,7 +4283,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st /// An object that provides culture-specific formatting information. /// An instance of . /// null if a valid item with provided exists; with an error message otherwise. - public static global::Thinktecture.ValidationError? Validate([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + public static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) { if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) { @@ -3756,6 +4294,7 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); } } + #endif /// /// Implicit conversion to the type . @@ -4095,7 +4634,13 @@ public TResult MapPartially( return @default; } - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.OrdinalIgnoreCase); @@ -4117,7 +4662,12 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif #else return lookup; #endif @@ -4156,6 +4706,9 @@ public partial class TestEnum [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] sealed partial class TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError>, + #endif global::System.IEquatable { [global::System.Runtime.CompilerServices.ModuleInitializer] @@ -4173,11 +4726,19 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -4239,7 +4800,7 @@ static partial void ValidateConstructorArguments( if (@key is null) return default; - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::TestEnum), @key); } @@ -4247,6 +4808,24 @@ static partial void ValidateConstructorArguments( return item; } + #if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + /// If there is no item with the provided . + public static global::TestEnum Get(global::System.ReadOnlySpan @key) + { + if (!_lookups.Value.Item2.TryGetValue(@key, out var item)) + { + throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::TestEnum), @key.ToString()); + } + + return item; + } + #endif + /// /// Gets a valid enumeration item for provided if a valid item exists. /// @@ -4261,8 +4840,21 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st return false; } - return _itemsLookup.Value.TryGetValue(@key, out item); + return _itemsLookup.TryGetValue(@key, out item); + } + + #if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::TestEnum item) + { + return _lookups.Value.Item2.TryGetValue(@key, out item); } + #endif /// /// Validates the provided and returns a valid enumeration item if found. @@ -4283,6 +4875,27 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st } } + #if NET9_0_OR_GREATER + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::TestEnum item) + { + if(global::TestEnum.TryGet(@key, out item)) + { + return null; + } + else + { + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); + } + } + #endif + /// /// Implicit conversion to the type . /// @@ -4621,7 +5234,13 @@ public TResult MapPartially( return @default; } - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.OrdinalIgnoreCase); @@ -4643,7 +5262,12 @@ void AddItem(global::TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif #else return lookup; #endif @@ -4686,6 +5310,9 @@ public int CompareTo(global::TestEnum? obj) partial class TestEnum : global::System.IParsable + #if NET9_0_OR_GREATER + , global::System.ISpanParsable + #endif { private static global::Thinktecture.ValidationError? Validate(string key, global::System.IFormatProvider? provider, out global::TestEnum? result) where T : global::Thinktecture.IValueObjectFactory @@ -4693,6 +5320,14 @@ partial class TestEnum : return T.Validate(key, provider, out result); } + #if NET9_0_OR_GREATER + private static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan key, global::System.IFormatProvider? provider, out global::TestEnum? result) + where T : global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError> + { + return T.Validate(key, provider, out result); + } + #endif + /// public static global::TestEnum Parse(string s, global::System.IFormatProvider? provider) { @@ -4704,6 +5339,19 @@ partial class TestEnum : throw new global::System.FormatException(validationError.ToString() ?? "Unable to parse \"TestEnum\"."); } + #if NET9_0_OR_GREATER + /// + public static global::TestEnum Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider? provider) + { + var validationError = Validate(s, provider, out var result); + + if(validationError is null) + return result!; + + throw new global::System.FormatException(validationError.ToString() ?? "Unable to parse \"TestEnum\"."); + } + #endif + /// public static bool TryParse( string? s, @@ -4719,6 +5367,18 @@ public static bool TryParse( var validationError = Validate(s, provider, out result!); return validationError is null; } + + #if NET9_0_OR_GREATER + /// + public static bool TryParse( + global::System.ReadOnlySpan s, + global::System.IFormatProvider? provider, + [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::TestEnum result) + { + var validationError = Validate(s, provider, out result!); + return validationError is null; + } + #endif } """); @@ -4873,6 +5533,9 @@ namespace Thinktecture.Tests { [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] partial class TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError>, + #endif global::System.IEquatable { [global::System.Runtime.CompilerServices.ModuleInitializer] @@ -4890,11 +5553,19 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -4956,7 +5627,7 @@ static partial void ValidateConstructorArguments( if (@key is null) return default; - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); } @@ -4964,6 +5635,24 @@ static partial void ValidateConstructorArguments( return item; } + #if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + /// If there is no item with the provided . + public static global::Thinktecture.Tests.TestEnum Get(global::System.ReadOnlySpan @key) + { + if (!_lookups.Value.Item2.TryGetValue(@key, out var item)) + { + throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key.ToString()); + } + + return item; + } + #endif + /// /// Gets a valid enumeration item for provided if a valid item exists. /// @@ -4978,8 +5667,21 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st return false; } - return _itemsLookup.Value.TryGetValue(@key, out item); + return _itemsLookup.TryGetValue(@key, out item); + } + + #if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + { + return _lookups.Value.Item2.TryGetValue(@key, out item); } + #endif /// /// Validates the provided and returns a valid enumeration item if found. @@ -5000,6 +5702,27 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st } } + #if NET9_0_OR_GREATER + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + { + if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) + { + return null; + } + else + { + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); + } + } + #endif + /// /// Implicit conversion to the type . /// @@ -5633,7 +6356,13 @@ public TResult MapPartially( return @default; } - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary(7, global::System.StringComparer.OrdinalIgnoreCase); @@ -5660,7 +6389,12 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item_derived_2, nameof(@Item_derived_2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif #else return lookup; #endif @@ -5728,6 +6462,9 @@ namespace Thinktecture.Tests { [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] sealed partial class TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError>, + #endif global::Thinktecture.IValidatableEnum, global::System.IEquatable { @@ -5746,11 +6483,19 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -5834,7 +6579,7 @@ static partial void ValidateConstructorArguments( if (@key is null) return default; - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { item = CreateAndCheckInvalidItem(@key); } @@ -5842,6 +6587,23 @@ static partial void ValidateConstructorArguments( return item; } + #if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + public static global::Thinktecture.Tests.TestEnum Get(global::System.ReadOnlySpan @key) + { + if (!_lookups.Value.Item2.TryGetValue(@key, out var item)) + { + item = CreateAndCheckInvalidItem(@key.ToString()); + } + + return item; + } + #endif + private static global::Thinktecture.Tests.TestEnum CreateAndCheckInvalidItem(string @key) { var item = CreateInvalidItem(@key); @@ -5874,13 +6636,30 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st return false; } - if(_itemsLookup.Value.TryGetValue(@key, out item)) + if(_itemsLookup.TryGetValue(@key, out item)) return true; item = CreateAndCheckInvalidItem(@key); return false; } + #if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + { + if(_lookups.Value.Item2.TryGetValue(@key, out item)) + return true; + + item = CreateAndCheckInvalidItem(@key.ToString()); + return false; + } + #endif + /// /// Validates the provided and returns a valid enumeration item if found. /// @@ -5902,6 +6681,28 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st } } + #if NET9_0_OR_GREATER + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + { + if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) + { + return null; + } + else + { + item = CreateAndCheckInvalidItem(@key.ToString()); + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); + } + } + #endif + /// /// Implicit conversion to the type . /// @@ -6340,7 +7141,13 @@ public TResult MapPartially( return @default; } - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.OrdinalIgnoreCase); @@ -6365,7 +7172,12 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif #else return lookup; #endif @@ -6412,6 +7224,9 @@ namespace Thinktecture.Tests [global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Auto)] [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] readonly partial struct TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError>, + #endif global::Thinktecture.IValidatableEnum, global::System.IEquatable { @@ -6430,11 +7245,19 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -6517,7 +7340,7 @@ static partial void ValidateConstructorArguments( if (@key is null) return default; - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { item = CreateAndCheckInvalidItem(@key); } @@ -6525,6 +7348,23 @@ static partial void ValidateConstructorArguments( return item; } + #if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + public static global::Thinktecture.Tests.TestEnum Get(global::System.ReadOnlySpan @key) + { + if (!_lookups.Value.Item2.TryGetValue(@key, out var item)) + { + item = CreateAndCheckInvalidItem(@key.ToString()); + } + + return item; + } + #endif + private static global::Thinktecture.Tests.TestEnum CreateAndCheckInvalidItem(string @key) { var item = CreateInvalidItem(@key); @@ -6546,20 +7386,37 @@ static partial void ValidateConstructorArguments( /// The identifier to return an enumeration item for. /// An instance of . /// true if a valid item with provided exists; false otherwise. - public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] string @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + { + if (@key is null) + { + item = default; + return false; + } + + if(_itemsLookup.TryGetValue(@key, out item)) + return true; + + item = CreateAndCheckInvalidItem(@key); + return false; + } + + #if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) { - if (@key is null) - { - item = default; - return false; - } - - if(_itemsLookup.Value.TryGetValue(@key, out item)) + if(_lookups.Value.Item2.TryGetValue(@key, out item)) return true; - item = CreateAndCheckInvalidItem(@key); + item = CreateAndCheckInvalidItem(@key.ToString()); return false; } + #endif /// /// Validates the provided and returns a valid enumeration item if found. @@ -6582,6 +7439,28 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st } } + #if NET9_0_OR_GREATER + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + { + if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) + { + return null; + } + else + { + item = CreateAndCheckInvalidItem(@key.ToString()); + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); + } + } + #endif + /// /// Implicit conversion to the type . /// @@ -7011,7 +7890,13 @@ public TResult MapPartially( return @default; } - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.OrdinalIgnoreCase); @@ -7033,7 +7918,12 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif #else return lookup; #endif @@ -7113,6 +8003,9 @@ namespace Thinktecture.Tests; partial struct TestEnum : global::System.IParsable + #if NET9_0_OR_GREATER + , global::System.ISpanParsable + #endif { private static global::Thinktecture.ValidationError? Validate(string key, global::System.IFormatProvider? provider, out global::Thinktecture.Tests.TestEnum result) where T : global::Thinktecture.IValueObjectFactory @@ -7120,6 +8013,14 @@ partial struct TestEnum : return T.Validate(key, provider, out result); } + #if NET9_0_OR_GREATER + private static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan key, global::System.IFormatProvider? provider, out global::Thinktecture.Tests.TestEnum result) + where T : global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError> + { + return T.Validate(key, provider, out result); + } + #endif + /// public static global::Thinktecture.Tests.TestEnum Parse(string s, global::System.IFormatProvider? provider) { @@ -7127,6 +8028,15 @@ partial struct TestEnum : return result!; } + #if NET9_0_OR_GREATER + /// + public static global::Thinktecture.Tests.TestEnum Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider? provider) + { + var validationError = Validate(s, provider, out var result); + return result!; + } + #endif + /// public static bool TryParse( string? s, @@ -7142,6 +8052,18 @@ public static bool TryParse( var validationError = Validate(s, provider, out result!); return true; } + + #if NET9_0_OR_GREATER + /// + public static bool TryParse( + global::System.ReadOnlySpan s, + global::System.IFormatProvider? provider, + [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum result) + { + var validationError = Validate(s, provider, out result!); + return true; + } + #endif } """); @@ -7219,6 +8141,9 @@ namespace Thinktecture.Tests { [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] sealed partial class TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError>, + #endif global::Thinktecture.IValidatableEnum, global::System.IEquatable { @@ -7237,11 +8162,19 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -7355,7 +8288,7 @@ static partial void ValidateConstructorArguments( if (@name is null) return default; - if (!_itemsLookup.Value.TryGetValue(@name, out var item)) + if (!_itemsLookup.TryGetValue(@name, out var item)) { item = CreateAndCheckInvalidItem(@name); } @@ -7363,6 +8296,23 @@ static partial void ValidateConstructorArguments( return item; } + #if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + public static global::Thinktecture.Tests.TestEnum Get(global::System.ReadOnlySpan @name) + { + if (!_lookups.Value.Item2.TryGetValue(@name, out var item)) + { + item = CreateAndCheckInvalidItem(@name.ToString()); + } + + return item; + } + #endif + private static global::Thinktecture.Tests.TestEnum CreateAndCheckInvalidItem(string @name) { var item = CreateInvalidItem(@name); @@ -7373,7 +8323,7 @@ static partial void ValidateConstructorArguments( if (item.IsValid) throw new global::System.Exception("The implementation of method 'CreateInvalidItem' must return an instance with property 'IsValid' equals to 'false'."); - if (_itemsLookup.Value.ContainsKey(item.Name)) + if (_itemsLookup.ContainsKey(item.Name)) throw new global::System.Exception("The implementation of method 'CreateInvalidItem' must not return an instance with property 'Name' equals to one of a valid item."); return item; @@ -7393,13 +8343,30 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st return false; } - if(_itemsLookup.Value.TryGetValue(@name, out item)) + if(_itemsLookup.TryGetValue(@name, out item)) return true; item = CreateAndCheckInvalidItem(@name); return false; } + #if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @name, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + { + if(_lookups.Value.Item2.TryGetValue(@name, out item)) + return true; + + item = CreateAndCheckInvalidItem(@name.ToString()); + return false; + } + #endif + /// /// Validates the provided and returns a valid enumeration item if found. /// @@ -7421,6 +8388,28 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st } } + #if NET9_0_OR_GREATER + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan @name, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + { + if(global::Thinktecture.Tests.TestEnum.TryGet(@name, out item)) + { + return null; + } + else + { + item = CreateAndCheckInvalidItem(@name.ToString()); + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@name}'."); + } + } + #endif + /// /// Implicit conversion to the type . /// @@ -7859,7 +8848,13 @@ public TResult MapPartially( return @default; } - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary(2, global::Thinktecture.ComparerAccessors.StringOrdinal.EqualityComparer); @@ -7884,7 +8879,12 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::Thinktecture.ComparerAccessors.StringOrdinal.EqualityComparer); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::Thinktecture.ComparerAccessors.StringOrdinal.EqualityComparer); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif #else return lookup; #endif @@ -8168,7 +9168,48 @@ public partial class TestEnum AssertOutput(formattable, _FORMATTABLE_OUTPUT_CLASS); AssertOutput(comparableOutput, _COMPARABLE_OUTPUT_CLASS_INT_BASED); - AssertOutput(parsableOutput, _PARSABLE_OUTPUT_CLASS_STRING_BASED); // string-based due to [ValueObjectFactory] + AssertOutput(parsableOutput, _GENERATED_HEADER + """ + + namespace Thinktecture.Tests; + + partial class TestEnum : + global::System.IParsable + { + private static global::Thinktecture.ValidationError? Validate(string key, global::System.IFormatProvider? provider, out global::Thinktecture.Tests.TestEnum? result) + where T : global::Thinktecture.IValueObjectFactory + { + return T.Validate(key, provider, out result); + } + + /// + public static global::Thinktecture.Tests.TestEnum Parse(string s, global::System.IFormatProvider? provider) + { + var validationError = Validate(s, provider, out var result); + + if(validationError is null) + return result!; + + throw new global::System.FormatException(validationError.ToString() ?? "Unable to parse \"TestEnum\"."); + } + + /// + public static bool TryParse( + string? s, + global::System.IFormatProvider? provider, + [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum result) + { + if(s is null) + { + result = default; + return false; + } + + var validationError = Validate(s, provider, out result!); + return validationError is null; + } + } + + """); AssertOutput(equalityComparisonOperators, _EQUALITY_COMPARABLE_OPERATORS_CLASS); AssertOutput(comparisonOperatorsOutput, _COMPARISON_OPERATORS_INT_BASED); @@ -8196,11 +9237,12 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -8255,7 +9297,7 @@ static partial void ValidateConstructorArguments( /// If there is no item with the provided . public static global::Thinktecture.Tests.TestEnum Get(int @key) { - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); } @@ -8271,7 +9313,7 @@ static partial void ValidateConstructorArguments( /// true if a valid item with provided exists; false otherwise. public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] int @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) { - return _itemsLookup.Value.TryGetValue(@key, out item); + return _itemsLookup.TryGetValue(@key, out item); } /// @@ -8650,7 +9692,8 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup); + return frozenDictionary; #else return lookup; #endif @@ -8693,7 +9736,48 @@ public partial class TestEnum AssertOutput(formattable, _FORMATTABLE_OUTPUT_CLASS); AssertOutput(comparableOutput, _COMPARABLE_OUTPUT_CLASS_INT_BASED); - AssertOutput(parsableOutput, _PARSABLE_OUTPUT_CLASS_STRING_BASED); // string-based due to [ValueObjectFactory] + AssertOutput(parsableOutput, _GENERATED_HEADER + """ + + namespace Thinktecture.Tests; + + partial class TestEnum : + global::System.IParsable + { + private static global::Thinktecture.ValidationError? Validate(string key, global::System.IFormatProvider? provider, out global::Thinktecture.Tests.TestEnum? result) + where T : global::Thinktecture.IValueObjectFactory + { + return T.Validate(key, provider, out result); + } + + /// + public static global::Thinktecture.Tests.TestEnum Parse(string s, global::System.IFormatProvider? provider) + { + var validationError = Validate(s, provider, out var result); + + if(validationError is null) + return result!; + + throw new global::System.FormatException(validationError.ToString() ?? "Unable to parse \"TestEnum\"."); + } + + /// + public static bool TryParse( + string? s, + global::System.IFormatProvider? provider, + [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum result) + { + if(s is null) + { + result = default; + return false; + } + + var validationError = Validate(s, provider, out result!); + return validationError is null; + } + } + + """); AssertOutput(equalityComparisonOperators, _EQUALITY_COMPARABLE_OPERATORS_CLASS); AssertOutput(comparisonOperatorsOutput, _COMPARISON_OPERATORS_INT_BASED); @@ -8722,11 +9806,12 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -8781,7 +9866,7 @@ static partial void ValidateConstructorArguments( /// If there is no item with the provided . public static global::Thinktecture.Tests.TestEnum Get(int @key) { - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); } @@ -8797,7 +9882,7 @@ static partial void ValidateConstructorArguments( /// true if a valid item with provided exists; false otherwise. public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] int @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) { - return _itemsLookup.Value.TryGetValue(@key, out item); + return _itemsLookup.TryGetValue(@key, out item); } /// @@ -9176,7 +10261,8 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup); + return frozenDictionary; #else return lookup; #endif @@ -9280,6 +10366,9 @@ namespace Thinktecture.Tests { [global::System.ComponentModel.TypeConverter(typeof(global::Thinktecture.ValueObjectTypeConverter))] sealed partial class TestEnum : global::Thinktecture.IEnum, + #if NET9_0_OR_GREATER + global::Thinktecture.IValueObjectFactory, global::Thinktecture.ValidationError>, + #endif global::System.IEquatable { [global::System.Runtime.CompilerServices.ModuleInitializer] @@ -9297,11 +10386,19 @@ internal static void ModuleInit() global::Thinktecture.Internal.KeyedValueObjectMetadataLookup.AddMetadata(enumType, metadata); } - private static readonly global::System.Lazy> _itemsLookup + #if NET9_0_OR_GREATER + private static readonly global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)> _lookups + = new global::System.Lazy<(global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>)>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value.Item1; + #else + private static readonly global::System.Lazy> _lookups = new global::System.Lazy>(GetLookup, global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + private static global::System.Collections.Generic.IReadOnlyDictionary _itemsLookup => _lookups.Value; + #endif private static readonly global::System.Lazy> _items - = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Value.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); + = new global::System.Lazy>(() => global::System.Linq.Enumerable.ToList(_itemsLookup.Values).AsReadOnly(), global::System.Threading.LazyThreadSafetyMode.PublicationOnly); /// /// Gets all valid items. @@ -9367,7 +10464,7 @@ static partial void ValidateConstructorArguments( if (@key is null) return default; - if (!_itemsLookup.Value.TryGetValue(@key, out var item)) + if (!_itemsLookup.TryGetValue(@key, out var item)) { throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key); } @@ -9375,6 +10472,24 @@ static partial void ValidateConstructorArguments( return item; } + #if NET9_0_OR_GREATER + /// + /// Gets an enumeration item for provided . + /// + /// The identifier to return an enumeration item for. + /// An instance of if is not null; otherwise null. + /// If there is no item with the provided . + public static global::Thinktecture.Tests.TestEnum Get(global::System.ReadOnlySpan @key) + { + if (!_lookups.Value.Item2.TryGetValue(@key, out var item)) + { + throw new global::Thinktecture.UnknownEnumIdentifierException(typeof(global::Thinktecture.Tests.TestEnum), @key.ToString()); + } + + return item; + } + #endif + /// /// Gets a valid enumeration item for provided if a valid item exists. /// @@ -9389,8 +10504,21 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st return false; } - return _itemsLookup.Value.TryGetValue(@key, out item); + return _itemsLookup.TryGetValue(@key, out item); + } + + #if NET9_0_OR_GREATER + /// + /// Gets a valid enumeration item for provided if a valid item exists. + /// + /// The identifier to return an enumeration item for. + /// An instance of . + /// true if a valid item with provided exists; false otherwise. + public static bool TryGet(global::System.ReadOnlySpan @key, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out global::Thinktecture.Tests.TestEnum item) + { + return _lookups.Value.Item2.TryGetValue(@key, out item); } + #endif /// /// Validates the provided and returns a valid enumeration item if found. @@ -9411,6 +10539,27 @@ public static bool TryGet([global::System.Diagnostics.CodeAnalysis.AllowNull] st } } + #if NET9_0_OR_GREATER + /// + /// Validates the provided and returns a valid enumeration item if found. + /// + /// The identifier to return an enumeration item for. + /// An object that provides culture-specific formatting information. + /// An instance of . + /// null if a valid item with provided exists; with an error message otherwise. + public static global::Thinktecture.ValidationError? Validate(global::System.ReadOnlySpan @key, global::System.IFormatProvider? @provider, [global::System.Diagnostics.CodeAnalysis.MaybeNull] out global::Thinktecture.Tests.TestEnum item) + { + if(global::Thinktecture.Tests.TestEnum.TryGet(@key, out item)) + { + return null; + } + else + { + return global::Thinktecture.Internal.ValidationErrorCreator.CreateValidationError($"There is no item of type 'TestEnum' with the identifier '{@key}'."); + } + } + #endif + /// /// Implicit conversion to the type . /// @@ -9578,7 +10727,13 @@ public TResult Map( } } - private static global::System.Collections.Generic.IReadOnlyDictionary GetLookup() + private static + #if NET9_0_OR_GREATER + (global::System.Collections.Generic.IReadOnlyDictionary, global::System.Collections.Frozen.FrozenDictionary.AlternateLookup>) + #else + global::System.Collections.Generic.IReadOnlyDictionary + #endif + GetLookup() { var lookup = new global::System.Collections.Generic.Dictionary(2, global::System.StringComparer.OrdinalIgnoreCase); @@ -9600,7 +10755,12 @@ void AddItem(global::Thinktecture.Tests.TestEnum item, string itemName) AddItem(@Item2, nameof(@Item2)); #if NET8_0_OR_GREATER - return global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + var frozenDictionary = global::System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary(lookup, global::System.StringComparer.OrdinalIgnoreCase); + #if NET9_0_OR_GREATER + return (frozenDictionary, frozenDictionary.GetAlternateLookup>()); + #else + return frozenDictionary; + #endif #else return lookup; #endif diff --git a/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Get.cs b/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Get.cs index 81cca216..40c5be56 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Get.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Get.cs @@ -26,7 +26,7 @@ public void Should_return_invalid_item_if_enum_doesnt_have_any_items() public void Should_return_invalid_item_via_reflection_if_enum_doesnt_have_any_items() { // ReSharper disable once PossibleNullReferenceException - var item = (EmptyEnum)typeof(EmptyEnum).GetMethod("Get", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy) + var item = (EmptyEnum)typeof(EmptyEnum).GetMethod("Get", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy, [typeof(string)]) .Invoke(null, new object[] { "unknown" }); item.Should().NotBeNull(); @@ -42,6 +42,16 @@ public void Should_return_invalid_item_if_enum_doesnt_have_item_with_provided_ke item.Key.Should().Be("unknown"); } +#if NET9_0_OR_GREATER + [Fact] + public void Should_return_item_having_ReadOnlySpanOfChar() + { + var item = TestEnum.Get(TestEnum.Item1.Key.AsSpan()); + item.IsValid.Should().BeTrue(); + item.Key.Should().Be(TestEnum.Item1.Key); + } +#endif + [Fact] public void Should_throw_if_CreateInvalidItem_uses_key_of_valid_item() { diff --git a/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Parse.cs b/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Parse.cs index b04584ce..dee40fa3 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Parse.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Parse.cs @@ -14,6 +14,14 @@ public void Should_parse_valid_value() TestSmartEnum_Class_DecimalBased.Parse("1.0", null).Should().Be(TestSmartEnum_Class_DecimalBased.Value1); } +#if NET9_0_OR_GREATER + [Fact] + public void Should_parse_ReadOnlySpanOfChar() + { + TestEnum.Parse(TestEnum.Item1.Key.AsSpan(), null).Should().Be(TestEnum.Item1); + } +#endif + [Fact] public void Should_use_format_provider_parse_valid_value() { diff --git a/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/TryGet.cs b/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/TryGet.cs index 96652a57..e478d6f1 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/TryGet.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/TryGet.cs @@ -1,3 +1,4 @@ +using System; using Thinktecture.Runtime.Tests.TestEnums; namespace Thinktecture.Runtime.Tests.EnumTests; @@ -37,6 +38,15 @@ public void Should_return_false_if_enum_dont_have_item_with_provided_key() item!.Key.Should().Be("unknown"); } +#if NET9_0_OR_GREATER + [Fact] + public void Should_return_item_having_ReadOnlySpanOfChar() + { + TestEnum.TryGet(TestEnum.Item1.Key.AsSpan(), out var item).Should().BeTrue(); + item.Should().Be(TestEnum.Item1); + } +#endif + [Fact] public void Should_return_true_if_item_with_provided_key_exists() { diff --git a/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/TryParse.cs b/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/TryParse.cs index fd697b44..b7a9422f 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/TryParse.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/TryParse.cs @@ -19,6 +19,15 @@ public void Should_parse_valid_value() caseSensitiveItem.Should().Be(TestEnumCaseSensitive.LowerCased); } +#if NET9_0_OR_GREATER + [Fact] + public void Should_parse_ReadOnlySpanOfChar() + { + TestEnum.TryParse(TestEnum.Item1.Key.AsSpan(), null, out var item).Should().BeTrue(); + item.Should().Be(TestEnum.Item1); + } +#endif + [Fact] public void Should_use_format_provider_parse_valid_value() { diff --git a/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Validate.cs b/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Validate.cs index 6678a0ce..835f8f8b 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Validate.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/EnumTests/Validate.cs @@ -34,6 +34,17 @@ public void Should_return_invalid_item_if_enum_doesnt_have_any_items() item.Key.Should().Be("unknown"); } +#if NET9_0_OR_GREATER + [Fact] + public void Should_return_item_having_ReadOnlySpanOfChar() + { + var validationError = TestEnum.Validate(TestEnum.Item1.Key.AsSpan(), null, out var item); + + validationError.Should().BeNull(); + item.Should().Be(TestEnum.Item1); + } +#endif + [Fact] public void Should_return_invalid_item_if_enum_doesnt_have_item_with_provided_key() {