From 068cb8cb34d5e862b704aa920637d589c7a684ca Mon Sep 17 00:00:00 2001 From: Jorge Rangel Date: Wed, 27 May 2026 14:44:40 -0500 Subject: [PATCH] fix: expand enum member value types --- .../InputTypes/InputEnumTypeIntegerValue.cs | 8 +- .../InputEnumTypeValueConverter.cs | 20 ++- .../tspCodeModel.json | 124 ++++++++++++++ .../test/TypeSpecInputConverterTests.cs | 151 ++++++++++++++++++ .../src/Providers/FixedEnumProvider.cs | 22 +++ .../EnumProviders/EnumProviderTests.cs | 90 +++++++++++ ...nlyEmittedForAllowedIntegralTypes(byte).cs | 12 ++ ...yEmittedForAllowedIntegralTypes(double).cs | 12 ++ ...lyEmittedForAllowedIntegralTypes(float).cs | 12 ++ ...OnlyEmittedForAllowedIntegralTypes(int).cs | 12 ++ ...nlyEmittedForAllowedIntegralTypes(long).cs | 12 ++ ...lyEmittedForAllowedIntegralTypes(sbyte).cs | 12 ++ ...lyEmittedForAllowedIntegralTypes(short).cs | 12 ++ ...yEmittedForAllowedIntegralTypes(string).cs | 12 ++ ...nlyEmittedForAllowedIntegralTypes(uint).cs | 12 ++ ...lyEmittedForAllowedIntegralTypes(ulong).cs | 12 ++ ...yEmittedForAllowedIntegralTypes(ushort).cs | 12 ++ .../ValidateGeneratedFloatFixedEnum.cs | 13 ++ .../ValidateGeneratedIntFixedEnum.cs | 13 ++ .../ValidateGeneratedLongFixedEnum.cs | 13 ++ .../test/common/InputFactory.cs | 33 ++++ 21 files changed, 615 insertions(+), 4 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TestData/TypeSpecInputConverterTests/LoadsEnumsWithIntegerAndLongValues/tspCodeModel.json create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(byte).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(double).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(float).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(int).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(long).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(sbyte).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(short).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(string).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(uint).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(ulong).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(ushort).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedFloatFixedEnum.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedIntFixedEnum.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedLongFixedEnum.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputEnumTypeIntegerValue.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputEnumTypeIntegerValue.cs index c18584f0c06..a8b5a33bbc2 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputEnumTypeIntegerValue.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputEnumTypeIntegerValue.cs @@ -11,6 +11,12 @@ public InputEnumTypeIntegerValue(string name, int integerValue, InputPrimitiveTy IntegerValue = integerValue; } - public int IntegerValue { get; } + public InputEnumTypeIntegerValue(string name, long integerValue, InputPrimitiveType valueType, string? summary, string? doc, InputEnumType? enumType = default) + : base(name, integerValue, valueType, summary, doc, enumType) + { + IntegerValue = integerValue; + } + + public long IntegerValue { get; } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputEnumTypeValueConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputEnumTypeValueConverter.cs index 5aed48c28d3..2623147484c 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputEnumTypeValueConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputEnumTypeValueConverter.cs @@ -59,9 +59,23 @@ internal static InputEnumTypeValue CreateEnumTypeValue(ref Utf8JsonReader reader InputEnumTypeValue enumValue = valueType.Kind switch { InputPrimitiveTypeKind.String => new InputEnumTypeStringValue(name, rawValue.Value.GetString() ?? throw new JsonException(), valueType, summary, doc, enumType) { Decorators = decorators ?? [] }, - InputPrimitiveTypeKind.Int32 => new InputEnumTypeIntegerValue(name, rawValue.Value.GetInt32(), valueType, summary, doc, enumType) { Decorators = decorators ?? [] }, - InputPrimitiveTypeKind.Float32 => new InputEnumTypeFloatValue(name, rawValue.Value.GetSingle(), valueType, summary, doc, enumType) { Decorators = decorators ?? [] }, - _ => throw new JsonException() + InputPrimitiveTypeKind.Integer or + InputPrimitiveTypeKind.Int8 or + InputPrimitiveTypeKind.Int16 or + InputPrimitiveTypeKind.Int32 or + InputPrimitiveTypeKind.UInt8 or + InputPrimitiveTypeKind.UInt16 => new InputEnumTypeIntegerValue(name, rawValue.Value.GetInt32(), valueType, summary, doc, enumType) { Decorators = decorators ?? [] }, + InputPrimitiveTypeKind.Int64 or + InputPrimitiveTypeKind.UInt32 or + InputPrimitiveTypeKind.UInt64 or + InputPrimitiveTypeKind.SafeInt => new InputEnumTypeIntegerValue(name, rawValue.Value.GetInt64(), valueType, summary, doc, enumType) { Decorators = decorators ?? [] }, + InputPrimitiveTypeKind.Float or + InputPrimitiveTypeKind.Float32 or + InputPrimitiveTypeKind.Float64 or + InputPrimitiveTypeKind.Numeric or + InputPrimitiveTypeKind.Decimal or + InputPrimitiveTypeKind.Decimal128 => new InputEnumTypeFloatValue(name, rawValue.Value.GetSingle(), valueType, summary, doc, enumType) { Decorators = decorators ?? [] }, + _ => throw new JsonException($"Unsupported enum valueType kind '{valueType.Kind}' for enum '{enumType.Name}' value '{name}'.") }; if (id != null) { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TestData/TypeSpecInputConverterTests/LoadsEnumsWithIntegerAndLongValues/tspCodeModel.json b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TestData/TypeSpecInputConverterTests/LoadsEnumsWithIntegerAndLongValues/tspCodeModel.json new file mode 100644 index 00000000000..f1c5032b076 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TestData/TypeSpecInputConverterTests/LoadsEnumsWithIntegerAndLongValues/tspCodeModel.json @@ -0,0 +1,124 @@ +{ + "$id": "1", + "name": "TestNamespace", + "models": [], + "clients": [], + "enums": [ + { + "$id": "2", + "kind": "enum", + "name": "WeatherIconCode", + "namespace": "TestNamespace", + "crossLanguageDefinitionId": "TestNamespace.WeatherIconCode", + "valueType": { + "$id": "3", + "kind": "integer", + "name": "integer", + "crossLanguageDefinitionId": "TypeSpec.integer", + "decorators": [] + }, + "values": [ + { + "$id": "4", + "kind": "enumvalue", + "name": "Sunny", + "value": 1, + "valueType": { "$ref": "3" }, + "enumType": { "$ref": "2" }, + "doc": "Sunny.", + "decorators": [] + }, + { + "$id": "5", + "kind": "enumvalue", + "name": "MostlySunny", + "value": 2, + "valueType": { "$ref": "3" }, + "enumType": { "$ref": "2" }, + "doc": "Mostly sunny.", + "decorators": [] + } + ], + "isFixed": false, + "isFlags": false, + "usage": "Output,Json", + "decorators": [] + }, + { + "$id": "6", + "kind": "enum", + "name": "Int32WeatherCode", + "namespace": "TestNamespace", + "crossLanguageDefinitionId": "TestNamespace.Int32WeatherCode", + "valueType": { + "$id": "7", + "kind": "int32", + "name": "int32", + "crossLanguageDefinitionId": "TypeSpec.int32", + "decorators": [] + }, + "values": [ + { + "$id": "8", + "kind": "enumvalue", + "name": "Cold", + "value": -10, + "valueType": { "$ref": "7" }, + "enumType": { "$ref": "6" }, + "decorators": [] + }, + { + "$id": "9", + "kind": "enumvalue", + "name": "Hot", + "value": 100, + "valueType": { "$ref": "7" }, + "enumType": { "$ref": "6" }, + "decorators": [] + } + ], + "isFixed": true, + "isFlags": false, + "usage": "Output,Json", + "decorators": [] + }, + { + "$id": "10", + "kind": "enum", + "name": "LongWeatherTimestamp", + "namespace": "TestNamespace", + "crossLanguageDefinitionId": "TestNamespace.LongWeatherTimestamp", + "valueType": { + "$id": "11", + "kind": "int64", + "name": "int64", + "crossLanguageDefinitionId": "TypeSpec.int64", + "decorators": [] + }, + "values": [ + { + "$id": "12", + "kind": "enumvalue", + "name": "Epoch", + "value": 0, + "valueType": { "$ref": "11" }, + "enumType": { "$ref": "10" }, + "decorators": [] + }, + { + "$id": "13", + "kind": "enumvalue", + "name": "MaxValue", + "value": 9223372036854775807, + "valueType": { "$ref": "11" }, + "enumType": { "$ref": "10" }, + "decorators": [] + } + ], + "isFixed": true, + "isFlags": false, + "usage": "Output,Json", + "decorators": [] + } + ] +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputConverterTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputConverterTests.cs index f2852315a57..606b0b91f80 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputConverterTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputConverterTests.cs @@ -690,6 +690,52 @@ public void LoadsModelWithExternalMetadataEndToEnd() Assert.AreEqual("8.0.0", externalModel.External.MinVersion); } + [Test] + public void LoadsEnumsWithIntegerAndLongValues() + { + var directory = Helpers.GetAssetFileOrDirectoryPath(false); + var content = File.ReadAllText(Path.Combine(directory, "tspCodeModel.json")); + var inputNamespace = TypeSpecSerialization.Deserialize(content); + + Assert.IsNotNull(inputNamespace); + Assert.AreEqual(3, inputNamespace!.Enums.Count); + + // 1) Base `integer` kind + var integerEnum = inputNamespace.Enums.SingleOrDefault(e => e.Name == "WeatherIconCode"); + Assert.IsNotNull(integerEnum); + Assert.AreEqual(InputPrimitiveTypeKind.Integer, integerEnum!.ValueType.Kind); + Assert.AreEqual(2, integerEnum.Values.Count); + var sunny = integerEnum.Values[0] as InputEnumTypeIntegerValue; + Assert.IsNotNull(sunny); + Assert.AreEqual("Sunny", sunny!.Name); + Assert.AreEqual(1L, sunny.IntegerValue); + var mostlySunny = integerEnum.Values[1] as InputEnumTypeIntegerValue; + Assert.IsNotNull(mostlySunny); + Assert.AreEqual(2L, mostlySunny!.IntegerValue); + + // 2) Explicit int32 kind, including a negative value. + var int32Enum = inputNamespace.Enums.SingleOrDefault(e => e.Name == "Int32WeatherCode"); + Assert.IsNotNull(int32Enum); + Assert.AreEqual(InputPrimitiveTypeKind.Int32, int32Enum!.ValueType.Kind); + var cold = int32Enum.Values[0] as InputEnumTypeIntegerValue; + Assert.IsNotNull(cold); + Assert.AreEqual(-10L, cold!.IntegerValue); + var hot = int32Enum.Values[1] as InputEnumTypeIntegerValue; + Assert.IsNotNull(hot); + Assert.AreEqual(100L, hot!.IntegerValue); + + // 3) int64 (long) kind, including long.MaxValue to confirm we use GetInt64. + var longEnum = inputNamespace.Enums.SingleOrDefault(e => e.Name == "LongWeatherTimestamp"); + Assert.IsNotNull(longEnum); + Assert.AreEqual(InputPrimitiveTypeKind.Int64, longEnum!.ValueType.Kind); + var epoch = longEnum.Values[0] as InputEnumTypeIntegerValue; + Assert.IsNotNull(epoch); + Assert.AreEqual(0L, epoch!.IntegerValue); + var maxValue = longEnum.Values[1] as InputEnumTypeIntegerValue; + Assert.IsNotNull(maxValue); + Assert.AreEqual(long.MaxValue, maxValue!.IntegerValue); + } + [Test] public void LoadsXmlOnlyModelDoesNotAddJsonUsage() { @@ -1131,5 +1177,110 @@ public void IgnoresUnknownPropertiesInSerializationOptions() Assert.IsNotNull(result!.Json); Assert.AreEqual("msg", result.Json!.Name); } + + private static JsonSerializerOptions CreateEnumValueTestOptions() + { + var referenceHandler = new TypeSpecReferenceHandler(); + return new JsonSerializerOptions + { + AllowTrailingCommas = true, + Converters = + { + new JsonStringEnumConverter(JsonNamingPolicy.CamelCase), + new InputTypeConverter(referenceHandler), + new InputEnumTypeConverter(referenceHandler), + new InputEnumTypeValueConverter(referenceHandler), + new InputPrimitiveTypeConverter(referenceHandler), + } + }; + } + + private static string CreateEnumJson(string valueKindJson, string rawValueJson) + { + return $$""" + { + "$id": "1", + "kind": "enum", + "name": "TestEnum", + "namespace": "Test.Models", + "crossLanguageDefinitionId": "Test.Models.TestEnum", + "valueType": { "$id": "2", "kind": {{valueKindJson}}, "name": "valueType", "crossLanguageDefinitionId": "TypeSpec.numeric" }, + "values": [ + { + "$id": "3", + "kind": "enumvalue", + "name": "One", + "value": {{rawValueJson}}, + "valueType": { "$ref": "2" }, + "enumType": { "$ref": "1" } + } + ], + "isFixed": true + } + """; + } + + [TestCase("\"integer\"", "1", 1L)] + [TestCase("\"int8\"", "1", 1L)] + [TestCase("\"int16\"", "1", 1L)] + [TestCase("\"int32\"", "1", 1L)] + [TestCase("\"uint8\"", "1", 1L)] + [TestCase("\"uint16\"", "1", 1L)] + [TestCase("\"int64\"", "9223372036854775807", 9223372036854775807L)] + [TestCase("\"uint32\"", "4294967295", 4294967295L)] + [TestCase("\"uint64\"", "9223372036854775807", 9223372036854775807L)] + [TestCase("\"safeInt\"", "9007199254740991", 9007199254740991L)] + public void DeserializeEnumWithIntegerKind(string valueKindJson, string rawValueJson, long expected) + { + var json = CreateEnumJson(valueKindJson, rawValueJson); + var enumType = JsonSerializer.Deserialize(json, CreateEnumValueTestOptions()); + + Assert.IsNotNull(enumType); + Assert.AreEqual(1, enumType!.Values.Count); + var value = enumType.Values[0] as InputEnumTypeIntegerValue; + Assert.IsNotNull(value); + Assert.AreEqual(expected, value!.IntegerValue); + Assert.AreEqual("One", value.Name); + } + + [TestCase("\"float\"", "1.5", 1.5f)] + [TestCase("\"float32\"", "1.5", 1.5f)] + [TestCase("\"float64\"", "1.5", 1.5f)] + [TestCase("\"numeric\"", "1.5", 1.5f)] + [TestCase("\"decimal\"", "1.5", 1.5f)] + [TestCase("\"decimal128\"", "1.5", 1.5f)] + public void DeserializeEnumWithFloatKind(string valueKindJson, string rawValueJson, float expected) + { + var json = CreateEnumJson(valueKindJson, rawValueJson); + var enumType = JsonSerializer.Deserialize(json, CreateEnumValueTestOptions()); + + Assert.IsNotNull(enumType); + Assert.AreEqual(1, enumType!.Values.Count); + var value = enumType.Values[0] as InputEnumTypeFloatValue; + Assert.IsNotNull(value); + Assert.AreEqual(expected, value!.FloatValue); + Assert.AreEqual("One", value.Name); + } + + [Test] + public void DeserializeEnumWithStringKind() + { + var json = CreateEnumJson("\"string\"", "\"sunny\""); + var enumType = JsonSerializer.Deserialize(json, CreateEnumValueTestOptions()); + + Assert.IsNotNull(enumType); + Assert.AreEqual(1, enumType!.Values.Count); + var value = enumType.Values[0] as InputEnumTypeStringValue; + Assert.IsNotNull(value); + Assert.AreEqual("sunny", value!.StringValue); + } + + [Test] + public void DeserializeEnumWithUnsupportedKindThrows() + { + var json = CreateEnumJson("\"boolean\"", "true"); + Assert.Throws(() => + JsonSerializer.Deserialize(json, CreateEnumValueTestOptions())); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/FixedEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/FixedEnumProvider.cs index 713100c1cf1..8e57781340e 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/FixedEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/FixedEnumProvider.cs @@ -46,6 +46,28 @@ protected override TypeProvider[] BuildSerializationProviders() protected override TypeSignatureModifiers BuildDeclarationModifiers() => _modifiers; + // The set of types permitted as a C# enum's underlying type (CS1008): + // https://learn.microsoft.com/dotnet/csharp/misc/cs1008 + private static readonly HashSet _allowedEnumUnderlyingTypes = + [ + typeof(byte), + typeof(sbyte), + typeof(short), + typeof(ushort), + typeof(uint), + typeof(long), + typeof(ulong), + ]; + protected override CSharpType? BuildBaseType() + { + var underlying = EnumUnderlyingType; + if (!underlying.IsFrameworkType || !_allowedEnumUnderlyingTypes.Contains(underlying.FrameworkType)) + { + return null; + } + return underlying; + } + // we have to build the values first, because the corresponding fieldDeclaration of the values might need all of the existing values to avoid name conflicts protected override IReadOnlyList BuildEnumValues() { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs index 5f106dd3c05..df0e8c093aa 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.Linq; using System.Threading.Tasks; using Microsoft.TypeSpec.Generator.Expressions; @@ -566,5 +567,94 @@ private static void ValidateGetHashCodeMethod(EnumProvider enumType) "global::System.ComponentModel.EditorBrowsableState.Never", getHashCodeMethod.Signature.Attributes[0].Arguments[0].ToDisplayString()); } + + [Test] + public void ValidateGeneratedIntFixedEnum() + { + MockHelpers.LoadMockGenerator(createCSharpTypeCore: (inputType) => typeof(int)); + + var input = InputFactory.Int32Enum("WeatherIconCode", [ + ("Sunny", 1), + ("MostlySunny", 2), + ("PartlyCloudy", 3), + ]); + + var enumType = EnumProvider.Create(input); + var content = new TypeProviderWriter(enumType).Write().Content; + + Assert.AreEqual(Helpers.GetExpectedFromFile(), content); + } + + [Test] + public void ValidateGeneratedLongFixedEnum() + { + MockHelpers.LoadMockGenerator(createCSharpTypeCore: (inputType) => typeof(long)); + + var input = InputFactory.Int64Enum("WeatherTimestamp", [ + ("Epoch", 0L), + ("Y2K", 946684800L), + ("MaxValue", long.MaxValue), + ]); + + var enumType = EnumProvider.Create(input); + var content = new TypeProviderWriter(enumType).Write().Content; + + Assert.AreEqual(Helpers.GetExpectedFromFile(), content); + } + + [Test] + public void ValidateGeneratedFloatFixedEnum() + { + MockHelpers.LoadMockGenerator(createCSharpTypeCore: (inputType) => typeof(float)); + + var input = InputFactory.Float32Enum("TemperatureScale", [ + ("OneDotOne", 1.1f), + ("TwoDotTwo", 2.2f), + ("FourDotFour", 4.4f), + ]); + + var enumType = EnumProvider.Create(input); + var content = new TypeProviderWriter(enumType).Write().Content; + + Assert.AreEqual(Helpers.GetExpectedFromFile(), content); + } + + [TestCase("byte")] + [TestCase("sbyte")] + [TestCase("short")] + [TestCase("ushort")] + [TestCase("int")] + [TestCase("uint")] + [TestCase("long")] + [TestCase("ulong")] + [TestCase("float")] + [TestCase("double")] + [TestCase("string")] + public void FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(string underlyingKeyword) + { + var underlying = underlyingKeyword switch + { + "byte" => typeof(byte), + "sbyte" => typeof(sbyte), + "short" => typeof(short), + "ushort" => typeof(ushort), + "int" => typeof(int), + "uint" => typeof(uint), + "long" => typeof(long), + "ulong" => typeof(ulong), + "float" => typeof(float), + "double" => typeof(double), + "string" => typeof(string), + _ => throw new ArgumentOutOfRangeException(nameof(underlyingKeyword)), + }; + + MockHelpers.LoadMockGenerator(createCSharpTypeCore: (_) => underlying); + + var input = InputFactory.Int32Enum("MockEnum", [("One", 1), ("Two", 2)]); + var enumType = EnumProvider.Create(input); + var content = new TypeProviderWriter(enumType).Write().Content; + + Assert.AreEqual(Helpers.GetExpectedFromFile(underlyingKeyword), content); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(byte).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(byte).cs new file mode 100644 index 00000000000..ff0dadddb1e --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(byte).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum MockEnum : byte + { + One, + Two + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(double).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(double).cs new file mode 100644 index 00000000000..b9a99d6264b --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(double).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum MockEnum + { + One, + Two + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(float).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(float).cs new file mode 100644 index 00000000000..b9a99d6264b --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(float).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum MockEnum + { + One, + Two + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(int).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(int).cs new file mode 100644 index 00000000000..17c02a8ed10 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(int).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum MockEnum + { + One = 1, + Two = 2 + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(long).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(long).cs new file mode 100644 index 00000000000..4e94ce42f42 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(long).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum MockEnum : long + { + One = 1, + Two = 2 + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(sbyte).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(sbyte).cs new file mode 100644 index 00000000000..89b4ff80f50 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(sbyte).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum MockEnum : sbyte + { + One, + Two + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(short).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(short).cs new file mode 100644 index 00000000000..c5b38fec909 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(short).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum MockEnum : short + { + One, + Two + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(string).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(string).cs new file mode 100644 index 00000000000..b9a99d6264b --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(string).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum MockEnum + { + One, + Two + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(uint).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(uint).cs new file mode 100644 index 00000000000..e96283c1bf4 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(uint).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum MockEnum : uint + { + One, + Two + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(ulong).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(ulong).cs new file mode 100644 index 00000000000..2c018c52008 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(ulong).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum MockEnum : ulong + { + One, + Two + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(ushort).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(ushort).cs new file mode 100644 index 00000000000..d6e8ec60d04 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/FixedEnumBaseType_OnlyEmittedForAllowedIntegralTypes(ushort).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum MockEnum : ushort + { + One, + Two + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedFloatFixedEnum.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedFloatFixedEnum.cs new file mode 100644 index 00000000000..a64f6a12c41 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedFloatFixedEnum.cs @@ -0,0 +1,13 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum TemperatureScale + { + OneDotOne, + TwoDotTwo, + FourDotFour + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedIntFixedEnum.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedIntFixedEnum.cs new file mode 100644 index 00000000000..c41216c5537 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedIntFixedEnum.cs @@ -0,0 +1,13 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum WeatherIconCode + { + Sunny = 1, + MostlySunny = 2, + PartlyCloudy = 3 + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedLongFixedEnum.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedLongFixedEnum.cs new file mode 100644 index 00000000000..e6760eccf50 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/ValidateGeneratedLongFixedEnum.cs @@ -0,0 +1,13 @@ +// + +#nullable disable + +namespace Sample.Models +{ + public enum WeatherTimestamp : long + { + Epoch = 0L, + Y2K = 946684800L, + MaxValue = 9223372036854775807L + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs index 4e627e7ffa8..cbcc8e8e14b 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs @@ -19,6 +19,11 @@ public static InputEnumTypeValue Int32(string name, int value, InputEnumType enu return new InputEnumTypeValue(name, value, InputPrimitiveType.Int32, "", $"{name} description", enumType); } + public static InputEnumTypeValue Int64(string name, long value, InputEnumType enumType) + { + return new InputEnumTypeValue(name, value, InputPrimitiveType.Int64, "", $"{name} description", enumType); + } + public static InputEnumTypeValue Float32(string name, float value, InputEnumType enumType) { return new InputEnumTypeValue(name, value, InputPrimitiveType.Float32, "", $"{name} description", enumType); @@ -180,6 +185,34 @@ public static InputEnumType Int32Enum( return enumType; } + public static InputEnumType Int64Enum( + string name, + IEnumerable<(string Name, long Value)> values, + string access = "public", + InputModelTypeUsage usage = InputModelTypeUsage.Input | InputModelTypeUsage.Output, + bool isExtensible = false, + string clientNamespace = "Sample.Models", + InputExternalTypeMetadata? external = null) + { + var enumValues = new List(); + var enumType = Enum( + name, + InputPrimitiveType.Int64, + enumValues, + access: access, + usage: usage, + isExtensible: isExtensible, + clientNamespace: clientNamespace, + external: external); + + foreach (var (valueName, value) in values) + { + enumValues.Add(EnumMember.Int64(valueName, value, enumType)); + } + + return enumType; + } + public static InputEnumType Float32Enum( string name, IEnumerable<(string Name, float Value)> values,