Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Merge remote-tracking branch 'origin/main' into input-transform
  • Loading branch information
live1206 committed Dec 23, 2024
commit ca523ddcd3565ef01f40fab124960befcb2ce5d1
2 changes: 1 addition & 1 deletion eng/Packages.Data.props
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@
</ItemGroup>

<ItemGroup Condition="'$(IsGeneratorLibrary)' == 'true'">
<PackageReference Update="Microsoft.Generator.CSharp.ClientModel" Version="1.0.0-alpha.20241217.2" />
<PackageReference Update="Microsoft.Generator.CSharp.ClientModel" Version="1.0.0-alpha.20241219.2" />
</ItemGroup>

<!--
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Core;
using Azure.Generator.Primitives;
using Azure.Generator.Providers;
using Azure.Generator.Providers.Abstraction;
using Microsoft.Generator.CSharp.ClientModel;
using Microsoft.Generator.CSharp.ClientModel.Providers;
using Microsoft.Generator.CSharp.ClientModel.Snippets;
using Microsoft.Generator.CSharp.Expressions;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Primitives;
Expand All @@ -17,7 +15,6 @@
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Text.Json;
using static Microsoft.Generator.CSharp.Snippets.Snippet;

namespace Azure.Generator
{
Expand Down Expand Up @@ -67,7 +64,7 @@ public class AzureTypeFactory : ScmTypeFactory
InputPrimitiveType? primitiveType = inputType;
while (primitiveType != null)
{
if (KnownAzureTypes.PrimitiveTypes.TryGetValue(primitiveType.CrossLanguageDefinitionId, out var knownType))
if (KnownAzureTypes.TryGetPrimitiveType(primitiveType.CrossLanguageDefinitionId, out var knownType))
{
return knownType;
}
Expand All @@ -79,40 +76,34 @@ public class AzureTypeFactory : ScmTypeFactory
}

/// <inheritdoc/>
public override ValueExpression GetValueTypeDeserializationExpression(Type valueType, ScopedApi<JsonElement> element, SerializationFormat format)
public override ValueExpression DeserializeJsonValue(Type valueType, ScopedApi<JsonElement> element, SerializationFormat format)
{
var expression = GetValueTypeDeserializationExpressionCore(valueType, element, format);
return expression ?? base.GetValueTypeDeserializationExpression(valueType, element, format);
var expression = DeserializeJsonValueCore(valueType, element, format);
return expression ?? base.DeserializeJsonValue(valueType, element, format);
}

private ValueExpression? GetValueTypeDeserializationExpressionCore(
private ValueExpression? DeserializeJsonValueCore(
Type valueType,
ScopedApi<JsonElement> element,
SerializationFormat format)
{
return valueType switch
{
Type t when t == typeof(ResourceIdentifier) =>
New.Instance(valueType, element.GetString()),
_ => null,
};
return KnownAzureTypes.TryGetJsonDeserializationExpression(valueType, out var deserializationExpression) ?
deserializationExpression(new CSharpType(valueType), element, format) :
null;
}

/// <inheritdoc/>
public override MethodBodyStatement SerializeValueType(CSharpType type, SerializationFormat serializationFormat, ValueExpression value, Type valueType, ScopedApi<Utf8JsonWriter> utf8JsonWriter, ScopedApi<ModelReaderWriterOptions> mrwOptionsParameter)
public override MethodBodyStatement SerializeJsonValue(Type valueType, ValueExpression value, ScopedApi<Utf8JsonWriter> utf8JsonWriter, ScopedApi<ModelReaderWriterOptions> mrwOptionsParameter, SerializationFormat serializationFormat)
{
var statement = SerializeValueTypeCore(type, serializationFormat, value, valueType, utf8JsonWriter, mrwOptionsParameter);
return statement ?? base.SerializeValueType(type, serializationFormat, value, valueType, utf8JsonWriter, mrwOptionsParameter);
var statement = SerializeValueTypeCore(serializationFormat, value, valueType, utf8JsonWriter, mrwOptionsParameter);
return statement ?? base.SerializeJsonValue(valueType, value, utf8JsonWriter, mrwOptionsParameter, serializationFormat);
}

private MethodBodyStatement? SerializeValueTypeCore(CSharpType type, SerializationFormat serializationFormat, ValueExpression value, Type valueType, ScopedApi<Utf8JsonWriter> utf8JsonWriter, ScopedApi<ModelReaderWriterOptions> mrwOptionsParameter)
private MethodBodyStatement? SerializeValueTypeCore(SerializationFormat serializationFormat, ValueExpression value, Type valueType, ScopedApi<Utf8JsonWriter> utf8JsonWriter, ScopedApi<ModelReaderWriterOptions> mrwOptionsParameter)
{
return valueType switch
{
Type t when t == typeof(ResourceIdentifier) =>
utf8JsonWriter.WriteStringValue(value.Property(nameof(ResourceIdentifier.Name))),
_ => null,
};
return KnownAzureTypes.TryGetJsonSerializationExpression(valueType, out var serializationExpression) ?
serializationExpression(value, utf8JsonWriter, mrwOptionsParameter, serializationFormat) :
null;
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,47 @@
// Licensed under the MIT License.

using Azure.Core;
using Microsoft.Generator.CSharp.ClientModel.Snippets;
using Microsoft.Generator.CSharp.Expressions;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Primitives;
using Microsoft.Generator.CSharp.Snippets;
using Microsoft.Generator.CSharp.Statements;
using System;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Text.Json;
using static Microsoft.Generator.CSharp.Snippets.Snippet;

namespace Azure.Generator.Primitives
{
internal static class KnownAzureTypes
{
public delegate MethodBodyStatement SerializationExpression(ValueExpression value, ScopedApi<Utf8JsonWriter> writer, ScopedApi<ModelReaderWriterOptions> options, SerializationFormat format);
public delegate ValueExpression DeserializationExpression(CSharpType valueType, ScopedApi<JsonElement> element, SerializationFormat format);

private const string UuidId = "Azure.Core.uuid";
private const string IPv4AddressId = "Azure.Core.ipV4Address";
private const string IPv6AddressId = "Azure.Core.ipV6Address";
private const string ETagId = "Azure.Core.eTag";
private const string AzureLocationId = "Azure.Core.azureLocation";
private const string ArmIdId = "Azure.Core.armResourceIdentifier";

internal static readonly IReadOnlyDictionary<string, CSharpType> PrimitiveTypes = new Dictionary<string, CSharpType>
private static MethodBodyStatement SerializeTypeWithImplicitOperatorToString(ValueExpression value, ScopedApi<Utf8JsonWriter> writer, ScopedApi<ModelReaderWriterOptions> options, SerializationFormat format)
=> writer.WriteStringValue(value);

private static ValueExpression DeserializeNewInstanceStringLikeType(CSharpType valueType, ScopedApi<JsonElement> element, SerializationFormat format)
=> New.Instance(valueType, element.GetString());

private static MethodBodyStatement SerializeTypeWithToString(ValueExpression value, ScopedApi<Utf8JsonWriter> writer, ScopedApi<ModelReaderWriterOptions> options, SerializationFormat format)
=> writer.WriteStringValue(value.InvokeToString());

private static ValueExpression DeserializeParsableStringLikeType(CSharpType valueType, ScopedApi<JsonElement> element, SerializationFormat format)
=> Static(valueType).Invoke("Parse", element.GetString());

private static readonly IReadOnlyDictionary<string, CSharpType> _idToTypes = new Dictionary<string, CSharpType>
{
[UuidId] = typeof(Guid),
[IPv4AddressId] = typeof(IPAddress),
Expand All @@ -27,5 +51,29 @@ internal static class KnownAzureTypes
[AzureLocationId] = typeof(AzureLocation),
[ArmIdId] = typeof(ResourceIdentifier),
};

private static readonly IReadOnlyDictionary<Type, SerializationExpression> _typeToSerializationExpression = new Dictionary<Type, SerializationExpression>
{
[typeof(Guid)] = SerializeTypeWithImplicitOperatorToString,
[typeof(IPAddress)] = SerializeTypeWithToString,
[typeof(ETag)] = SerializeTypeWithToString,
[typeof(AzureLocation)] = SerializeTypeWithImplicitOperatorToString,
[typeof(ResourceIdentifier)] = SerializeTypeWithImplicitOperatorToString,
};

private static readonly IReadOnlyDictionary<Type, DeserializationExpression> _typeToDeserializationExpression = new Dictionary<Type, DeserializationExpression>
{
[typeof(Guid)] = DeserializeNewInstanceStringLikeType,
[typeof(IPAddress)] = DeserializeParsableStringLikeType,
[typeof(ETag)] = DeserializeNewInstanceStringLikeType,
[typeof(AzureLocation)] = DeserializeNewInstanceStringLikeType,
[typeof(ResourceIdentifier)] = DeserializeNewInstanceStringLikeType,
};

public static bool TryGetPrimitiveType(string id, [MaybeNullWhen(false)] out CSharpType type) => _idToTypes.TryGetValue(id, out type);

public static bool TryGetJsonSerializationExpression(Type type, [MaybeNullWhen(false)] out SerializationExpression expression) => _typeToSerializationExpression.TryGetValue(type, out expression);

public static bool TryGetJsonDeserializationExpression(Type type, [MaybeNullWhen(false)] out DeserializationExpression expression) => _typeToDeserializationExpression.TryGetValue(type, out expression);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@
using Azure.Core;
using Azure.Generator.Tests.Common;
using Azure.Generator.Tests.TestHelpers;
using Microsoft.Azure.Test.HttpRecorder;
using Microsoft.Generator.CSharp.Expressions;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Primitives;
using Microsoft.Generator.CSharp.Providers;
using Microsoft.Generator.CSharp.Snippets;
using NUnit.Framework;
using System;
using System.Buffers;
using System.ClientModel.Primitives;
using System.Net;
using System.Text.Json;

namespace Azure.Generator.Tests
{
Expand All @@ -20,6 +26,38 @@ public void SetUp()
MockHelpers.LoadMockPlugin();
}

[TestCase(typeof(Guid), ExpectedResult = "writer.WriteStringValue(value);\n")]
[TestCase(typeof(IPAddress), ExpectedResult ="writer.WriteStringValue(value.ToString());\n")]
[TestCase(typeof(ETag), ExpectedResult = "writer.WriteStringValue(value.ToString());\n")]
[TestCase(typeof(AzureLocation), ExpectedResult = "writer.WriteStringValue(value);\n")]
[TestCase(typeof(ResourceIdentifier), ExpectedResult = "writer.WriteStringValue(value);\n")]
public string ValidateSerializationStatement(Type type)
{
var value = new ParameterProvider("value", $"", type).AsExpression().As(type);
var writer = new ParameterProvider("writer", $"", typeof(Utf8JsonWriter)).AsExpression().As<Utf8JsonWriter>();
var options = new ParameterProvider("options", $"", typeof(ModelReaderWriterOptions)).AsExpression().As<ModelReaderWriterOptions>();

var statement = AzureClientPlugin.Instance.TypeFactory.SerializeJsonValue(type, value, writer, options, SerializationFormat.Default);
Assert.IsNotNull(statement);

return statement.ToDisplayString();
}

[TestCase(typeof(Guid), ExpectedResult = "new global::System.Guid(element.GetString())")]
[TestCase(typeof(IPAddress), ExpectedResult = "global::System.Net.IPAddress.Parse(element.GetString())")]
[TestCase(typeof(ETag), ExpectedResult = "new global::Azure.ETag(element.GetString())")]
[TestCase(typeof(AzureLocation), ExpectedResult = "new global::Azure.Core.AzureLocation(element.GetString())")]
[TestCase(typeof(ResourceIdentifier), ExpectedResult = "new global::Azure.Core.ResourceIdentifier(element.GetString())")]
public string ValidateDeserializationExpression(Type type)
{
var element = new ParameterProvider("element", $"", typeof(JsonElement)).AsExpression().As<JsonElement>();

var expression = AzureClientPlugin.Instance.TypeFactory.DeserializeJsonValue(type, element, SerializationFormat.Default);
Assert.IsNotNull(expression);

return expression.ToDisplayString();
}

[Test]
public void Uuid()
{
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.