diff --git a/logger/autorest.codemodel/AutoRest.CodeModel.csproj b/logger/autorest.codemodel/AutoRest.CodeModel.csproj
new file mode 100644
index 0000000..5f9f562
--- /dev/null
+++ b/logger/autorest.codemodel/AutoRest.CodeModel.csproj
@@ -0,0 +1,13 @@
+
+
+
+ Exe
+ net8.0
+ enable
+
+
+
+
+
+
+
diff --git a/logger/autorest.codemodel/CustomEnumNameGenerator.cs b/logger/autorest.codemodel/CustomEnumNameGenerator.cs
new file mode 100644
index 0000000..ee5aeb8
--- /dev/null
+++ b/logger/autorest.codemodel/CustomEnumNameGenerator.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using NJsonSchema;
+using NJsonSchema.CodeGeneration;
+
+namespace AutoRest.CodeModel
+{
+ internal class CustomEnumNameGenerator : IEnumNameGenerator
+ {
+ private readonly DefaultEnumNameGenerator _defaultEnumNameGenerator = new DefaultEnumNameGenerator();
+
+ public string Generate(int index, string name, object value, JsonSchema schema) =>
+ _defaultEnumNameGenerator.Generate(
+ index,
+ // Fixes + and - enum values that cannot be generated into C# enum names
+ name.Equals("+") ? "plus" : name.Equals("-") ? "minus" : name,
+ value,
+ schema);
+ }
+}
diff --git a/logger/autorest.codemodel/CustomPropertyNameGenerator.cs b/logger/autorest.codemodel/CustomPropertyNameGenerator.cs
new file mode 100644
index 0000000..c232df2
--- /dev/null
+++ b/logger/autorest.codemodel/CustomPropertyNameGenerator.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using NJsonSchema;
+using NJsonSchema.CodeGeneration.CSharp;
+
+namespace AutoRest.CodeModel
+{
+ internal class CustomPropertyNameGenerator : CSharpPropertyNameGenerator
+ {
+ public override string Generate(JsonSchemaProperty property)
+ {
+ // Makes array type properties initialized with empty collections
+ if (property.IsArray)
+ {
+ property.IsRequired = true;
+ }
+
+ // Cases CSharp properly
+ if (property.Name == "csharp")
+ {
+ return "CSharp";
+ }
+ return base.Generate(property);
+ }
+ }
+}
diff --git a/logger/autorest.codemodel/CustomTypeNameGenerator.cs b/logger/autorest.codemodel/CustomTypeNameGenerator.cs
new file mode 100644
index 0000000..032cfed
--- /dev/null
+++ b/logger/autorest.codemodel/CustomTypeNameGenerator.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using NJsonSchema;
+
+namespace AutoRest.CodeModel
+{
+ internal class CustomTypeNameGenerator : DefaultTypeNameGenerator
+ {
+ // Class names that conflict with project class names
+ private static readonly Dictionary RenameMap = new Dictionary
+ {
+ { "HttpHeader", "HttpResponseHeader" },
+ { "Parameter", "RequestParameter" },
+ { "Request", "ServiceRequest" },
+ { "Response", "ServiceResponse" },
+ { "SerializationFormat", "SerializationFormatMetadata" }
+ };
+
+ public override string Generate(JsonSchema schema, string typeNameHint, IEnumerable reservedTypeNames)
+ {
+ if (RenameMap.ContainsKey(typeNameHint))
+ {
+ typeNameHint = RenameMap[typeNameHint];
+ }
+ return base.Generate(schema, typeNameHint, reservedTypeNames);
+ }
+ }
+}
diff --git a/logger/autorest.codemodel/Program.cs b/logger/autorest.codemodel/Program.cs
new file mode 100644
index 0000000..0d50f39
--- /dev/null
+++ b/logger/autorest.codemodel/Program.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using NJsonSchema;
+using NJsonSchema.CodeGeneration.CSharp;
+
+namespace AutoRest.CodeModel
+{
+ internal static class Program
+ {
+ private const string Path = "AutoRest.CSharp/Common/Input/Generated";
+ private static readonly string Namespace = "AutoRest.CSharp.Input";
+
+ private static async Task Main()
+ {
+ using var webClient = new HttpClient();
+ var schemaJson = await webClient.GetStringAsync(@"https://raw.githubusercontent.com/Azure/autorest/master/packages/libs/codemodel/.resources/all-in-one/json/code-model.json");
+ schemaJson = schemaJson
+ // Makes Choices only have string values
+ .Replace(" \"type\": [\n \"string\",\n \"number\",\n \"boolean\"\n ]\n", $" \"type\": \"string\"\n");
+
+ var schema = JsonSchema.FromJsonAsync(schemaJson).GetAwaiter().GetResult();
+ var settings = new CSharpGeneratorSettings
+ {
+ Namespace = Namespace,
+ HandleReferences = true,
+ TypeAccessModifier = "internal",
+ TypeNameGenerator = new CustomTypeNameGenerator(),
+ PropertyNameGenerator = new CustomPropertyNameGenerator(),
+ EnumNameGenerator = new CustomEnumNameGenerator(),
+ ExcludedTypeNames = new[]
+ {
+ "GroupSchema"
+ }
+ };
+ var rawFile = new CSharpGenerator(schema, settings).GenerateFile();
+ var cleanFile = String.Join(Environment.NewLine, rawFile.ToLines()
+ // Converts Newtonsoft attributes to YamlDotNet attributes
+ .Where(l => !l.Contains("Newtonsoft.Json.JsonConverter")
+ && !l.Contains("Newtonsoft.Json.JsonExtensionData"))
+ .Select(l => Regex.Replace(l, @"(.*\[)Newtonsoft\.Json\.JsonProperty\((.*""),?.*(\)\])",
+ "$1YamlDotNet.Serialization.YamlMember(Alias = $2$3", RegexOptions.Singleline).TrimEnd()))
+ // Removes AdditionalProperties property from types as they are required to derive from IDictionary for deserialization to work properly
+ .Replace($" private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary();{Environment.NewLine}{Environment.NewLine} public System.Collections.Generic.IDictionary AdditionalProperties{Environment.NewLine} {{{Environment.NewLine} get {{ return _additionalProperties; }}{Environment.NewLine} set {{ _additionalProperties = value; }}{Environment.NewLine} }}{Environment.NewLine}", String.Empty)
+ // Fixes stray blank lines from the C# generator
+ .Replace($"{Environment.NewLine}{Environment.NewLine}{Environment.NewLine}", Environment.NewLine)
+ .Replace($"{Environment.NewLine}{Environment.NewLine} }}", $"{Environment.NewLine} }}")
+ // Weird generation issue workaround
+ .Replace($"{Namespace}.bool.True", "true");
+
+ var lines = cleanFile.ToLines().ToArray();
+ var fileWithNullable = String.Join(Environment.NewLine, lines.Zip(lines.Skip(1).Append(String.Empty))
+ .Select(ll =>
+ {
+ var isNullableProperty = !ll.First.Contains("System.ComponentModel.DataAnnotations.Required") && ll.Second.Contains("{ get; set; }");
+ return isNullableProperty ? Regex.Replace(ll.Second, @"( \w+ { get; set; })", "?$1") : ll.Second;
+ })
+ .SkipLast(1)
+ .Prepend(lines.First()));
+ File.WriteAllText($"../../src/{Path}/CodeModel.cs", fileWithNullable);
+ }
+ }
+}
diff --git a/logger/autorest.codemodel/Properties/launchSettings.json b/logger/autorest.codemodel/Properties/launchSettings.json
new file mode 100644
index 0000000..536fa07
--- /dev/null
+++ b/logger/autorest.codemodel/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "AutoRest.CodeModel": {
+ "commandName": "Project",
+ "workingDirectory": "$(ProjectDir)"
+ }
+ }
+}
\ No newline at end of file
diff --git a/logger/autorest.codemodel/StringExtensions.cs b/logger/autorest.codemodel/StringExtensions.cs
new file mode 100644
index 0000000..e77fca1
--- /dev/null
+++ b/logger/autorest.codemodel/StringExtensions.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace AutoRest.CodeModel
+{
+ internal static class StringExtensions
+ {
+ //https://stackoverflow.com/a/41176852/294804
+ public static IEnumerable ToLines(this string value, bool removeEmptyLines = false)
+ {
+ using var sr = new StringReader(value);
+ string? line;
+ while ((line = sr.ReadLine()) != null)
+ {
+ if (removeEmptyLines && String.IsNullOrWhiteSpace(line))
+ continue;
+ yield return line;
+ }
+ }
+ }
+}
diff --git a/logger/autorest.codemodel/code-model.json b/logger/autorest.codemodel/code-model.json
new file mode 100644
index 0000000..8849284
--- /dev/null
+++ b/logger/autorest.codemodel/code-model.json
@@ -0,0 +1,3317 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "definitions": {
+ "ApiVersion": {
+ "description": "- since API version formats range from\nAzure ARM API date style (2018-01-01) to semver (1.2.3)\nand virtually any other text, this value tends to be an\nopaque string with the possibility of a modifier to indicate\nthat it is a range.\n\noptions:\n- prepend a dash or append a plus to indicate a range\n(ie, '2018-01-01+' or '-2019-01-01', or '1.0+' )\n\n- semver-range style (ie, '^1.0.0' or '~1.0.0' )",
+ "type": "object",
+ "properties": {
+ "version": {
+ "description": "the actual api version string used in the API",
+ "type": "string"
+ },
+ "range": {
+ "enum": [
+ "+",
+ "-"
+ ],
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "version"
+ ]
+ },
+ "ApiVersions": {
+ "description": "a collection of api versions",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ApiVersion"
+ }
+ },
+ "Schema": {
+ "type": "object",
+ "properties": {
+ "language": {
+ "$ref": "#/definitions/Languages",
+ "description": "per-language information for Schema"
+ },
+ "type": {
+ "$ref": "#/definitions/AllSchemaTypes",
+ "description": "the schema type"
+ },
+ "summary": {
+ "description": "a short description",
+ "type": "string"
+ },
+ "example": {
+ "description": "example information"
+ },
+ "defaultValue": {
+ "description": "If the value isn't sent on the wire, the service will assume this"
+ },
+ "serialization": {
+ "$ref": "#/definitions/SerializationFormats",
+ "description": "per-serialization information for this Schema"
+ },
+ "apiVersions": {
+ "description": "API versions that this applies to. Undefined means all versions",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ApiVersion"
+ }
+ },
+ "deprecated": {
+ "$ref": "#/definitions/Deprecation",
+ "description": "Represent the deprecation information if api is deprecated.",
+ "default": "undefined"
+ },
+ "origin": {
+ "description": "where did this aspect come from (jsonpath or 'modelerfour:')",
+ "type": "string"
+ },
+ "externalDocs": {
+ "$ref": "#/definitions/ExternalDocumentation",
+ "description": "External Documentation Links"
+ },
+ "protocol": {
+ "$ref": "#/definitions/Protocols",
+ "description": "per-protocol information for this aspect"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ]
+ },
+ "Deprecation": {
+ "description": "Represent information about a deprecation",
+ "type": "object",
+ "properties": {
+ "reason": {
+ "description": "Reason why this was deprecated.",
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "Extensions": {
+ "description": "A dictionary of open-ended 'x-*' extensions propogated from the original source document.",
+ "type": "object",
+ "properties": {
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "uri": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "ExternalDocumentation": {
+ "description": "a reference to external documentation",
+ "type": "object",
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "url": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "url"
+ ]
+ },
+ "Languages": {
+ "description": "custom extensible metadata for individual language generators",
+ "type": "object",
+ "properties": {
+ "default": {
+ "$ref": "#/definitions/Language"
+ },
+ "csharp": {
+ "$ref": "#/definitions/Language"
+ },
+ "python": {
+ "$ref": "#/definitions/Language"
+ },
+ "ruby": {
+ "$ref": "#/definitions/Language"
+ },
+ "go": {
+ "$ref": "#/definitions/Language"
+ },
+ "typescript": {
+ "$ref": "#/definitions/Language"
+ },
+ "javascript": {
+ "$ref": "#/definitions/Language"
+ },
+ "powershell": {
+ "$ref": "#/definitions/Language"
+ },
+ "java": {
+ "$ref": "#/definitions/Language"
+ },
+ "c": {
+ "$ref": "#/definitions/Language"
+ },
+ "cpp": {
+ "$ref": "#/definitions/Language"
+ },
+ "swift": {
+ "$ref": "#/definitions/Language"
+ },
+ "objectivec": {
+ "$ref": "#/definitions/Language"
+ },
+ "sputnik": {
+ "$ref": "#/definitions/Language"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "default"
+ ]
+ },
+ "Protocols": {
+ "description": "custom extensible metadata for individual protocols (ie, HTTP, etc)",
+ "type": "object",
+ "properties": {
+ "http": {
+ "$ref": "#/definitions/Protocol"
+ },
+ "amqp": {
+ "$ref": "#/definitions/Protocol"
+ },
+ "mqtt": {
+ "$ref": "#/definitions/Protocol"
+ },
+ "jsonrpc": {
+ "$ref": "#/definitions/Protocol"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "Metadata": {
+ "description": "common pattern for Metadata on aspects",
+ "type": "object",
+ "properties": {
+ "language": {
+ "$ref": "#/definitions/Languages",
+ "description": "per-language information for this aspect"
+ },
+ "protocol": {
+ "$ref": "#/definitions/Protocols",
+ "description": "per-protocol information for this aspect"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol"
+ ]
+ },
+ "Language": {
+ "description": "the bare-minimum fields for per-language metadata on a given aspect",
+ "type": "object",
+ "properties": {
+ "name": {
+ "description": "name used in actual implementation",
+ "type": "string"
+ },
+ "description": {
+ "description": "description text - describes this node.",
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": {
+ "type": "object"
+ },
+ "required": [
+ "description",
+ "name"
+ ]
+ },
+ "CSharpLanguage": {
+ "type": "object",
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "Protocol": {
+ "description": "the bare-minimum fields for per-protocol metadata on a given aspect",
+ "type": "object",
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "email": {
+ "type": "string"
+ },
+ "Contact": {
+ "description": "contact information",
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "url": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "email": {
+ "type": "string"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "License": {
+ "description": "license information",
+ "type": "object",
+ "properties": {
+ "name": {
+ "description": "the nameof the license",
+ "type": "string"
+ },
+ "url": {
+ "description": "an uri pointing to the full license text",
+ "type": "string"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "name"
+ ]
+ },
+ "Info": {
+ "description": "code model information",
+ "type": "object",
+ "properties": {
+ "title": {
+ "description": "the title of this service.",
+ "type": "string"
+ },
+ "description": {
+ "description": "a text description of the service",
+ "type": "string"
+ },
+ "termsOfService": {
+ "description": "an uri to the terms of service specified to access the service",
+ "type": "string"
+ },
+ "contact": {
+ "$ref": "#/definitions/Contact",
+ "description": "contact information for the service"
+ },
+ "license": {
+ "$ref": "#/definitions/License",
+ "description": "license information for th service"
+ },
+ "externalDocs": {
+ "$ref": "#/definitions/ExternalDocumentation",
+ "description": "External Documentation"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "title"
+ ]
+ },
+ "XmlSerlializationFormat": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "namespace": {
+ "type": "string"
+ },
+ "prefix": {
+ "type": "string"
+ },
+ "attribute": {
+ "type": "boolean"
+ },
+ "wrapped": {
+ "type": "boolean"
+ },
+ "text": {
+ "type": "boolean"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "attribute",
+ "text",
+ "wrapped"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/SerializationFormat"
+ }
+ ]
+ },
+ "SerializationFormats": {
+ "description": "custom extensible metadata for individual serialization formats",
+ "type": "object",
+ "properties": {
+ "json": {
+ "$ref": "#/definitions/SerializationFormat"
+ },
+ "xml": {
+ "$ref": "#/definitions/XmlSerlializationFormat"
+ },
+ "protobuf": {
+ "$ref": "#/definitions/SerializationFormat"
+ },
+ "binary": {
+ "$ref": "#/definitions/SerializationFormat"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "SchemaType": {
+ "description": "possible schema types that indicate the type of schema.",
+ "enum": [
+ "any",
+ "any-object",
+ "arm-id",
+ "array",
+ "binary",
+ "boolean",
+ "byte-array",
+ "char",
+ "choice",
+ "conditional",
+ "constant",
+ "credential",
+ "date",
+ "date-time",
+ "dictionary",
+ "duration",
+ "flag",
+ "group",
+ "integer",
+ "not",
+ "number",
+ "object",
+ "odata-query",
+ "or",
+ "sealed-choice",
+ "sealed-conditional",
+ "string",
+ "time",
+ "unixtime",
+ "unknown",
+ "uri",
+ "uuid",
+ "xor"
+ ],
+ "type": "string"
+ },
+ "CompoundSchemaTypes": {
+ "description": "Compound schemas are used to construct complex objects or offer choices of a set of schemas.\n\n(ie, allOf, anyOf, oneOf )",
+ "enum": [
+ "or",
+ "xor"
+ ],
+ "type": "string"
+ },
+ "PrimitiveSchemaTypes": {
+ "description": "Schema types that are primitive language values",
+ "enum": [
+ "arm-id",
+ "boolean",
+ "char",
+ "credential",
+ "date",
+ "date-time",
+ "duration",
+ "integer",
+ "number",
+ "string",
+ "time",
+ "unixtime",
+ "uri",
+ "uuid"
+ ],
+ "type": "string"
+ },
+ "ValueSchemaTypes": {
+ "description": "schema types that are non-object or complex types",
+ "enum": [
+ "arm-id",
+ "array",
+ "boolean",
+ "byte-array",
+ "char",
+ "choice",
+ "conditional",
+ "credential",
+ "date",
+ "date-time",
+ "duration",
+ "flag",
+ "integer",
+ "number",
+ "sealed-choice",
+ "sealed-conditional",
+ "string",
+ "time",
+ "unixtime",
+ "uri",
+ "uuid"
+ ],
+ "type": "string"
+ },
+ "ObjectSchemaTypes": {
+ "description": "schema types that can be objects",
+ "enum": [
+ "dictionary",
+ "object",
+ "or"
+ ],
+ "type": "string"
+ },
+ "AllSchemaTypes": {
+ "description": "all schema types",
+ "enum": [
+ "any",
+ "any-object",
+ "arm-id",
+ "array",
+ "binary",
+ "boolean",
+ "byte-array",
+ "char",
+ "choice",
+ "conditional",
+ "constant",
+ "credential",
+ "date",
+ "date-time",
+ "dictionary",
+ "duration",
+ "flag",
+ "group",
+ "integer",
+ "not",
+ "number",
+ "object",
+ "odata-query",
+ "or",
+ "sealed-choice",
+ "sealed-conditional",
+ "string",
+ "time",
+ "unixtime",
+ "uri",
+ "uuid",
+ "xor"
+ ],
+ "type": "string"
+ },
+ "SerializationFormat": {
+ "type": "object",
+ "properties": {
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "ValueSchema": {
+ "description": "schema types that are non-object or complex types",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Schema"
+ }
+ ]
+ },
+ "PrimitiveSchema": {
+ "description": "Schema types that are primitive language values",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/ValueSchema"
+ }
+ ]
+ },
+ "ComplexSchema": {
+ "description": "schema types that can be objects",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Schema"
+ }
+ ]
+ },
+ "Value": {
+ "description": "common base interface for properties, parameters and the like.",
+ "type": "object",
+ "properties": {
+ "schema": {
+ "$ref": "#/definitions/Schema",
+ "description": "the schema of this Value"
+ },
+ "required": {
+ "description": "if the value is marked 'required'.",
+ "type": "boolean"
+ },
+ "nullable": {
+ "description": "can null be passed in instead",
+ "type": "boolean"
+ },
+ "assumedValue": {
+ "description": "the value that the remote will assume if this value is not present"
+ },
+ "clientDefaultValue": {
+ "description": "the value that the client should provide if the consumer doesn't provide one"
+ },
+ "summary": {
+ "description": "a short description",
+ "type": "string"
+ },
+ "apiVersions": {
+ "description": "API versions that this applies to. Undefined means all versions",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ApiVersion"
+ }
+ },
+ "deprecated": {
+ "$ref": "#/definitions/Deprecation",
+ "description": "Represent the deprecation information if api is deprecated.",
+ "default": "undefined"
+ },
+ "origin": {
+ "description": "where did this aspect come from (jsonpath or 'modelerfour:')",
+ "type": "string"
+ },
+ "externalDocs": {
+ "$ref": "#/definitions/ExternalDocumentation",
+ "description": "External Documentation Links"
+ },
+ "language": {
+ "$ref": "#/definitions/Languages",
+ "description": "per-language information for this aspect"
+ },
+ "protocol": {
+ "$ref": "#/definitions/Protocols",
+ "description": "per-protocol information for this aspect"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "schema"
+ ]
+ },
+ "Property": {
+ "description": "a property is a child value in an object",
+ "type": "object",
+ "properties": {
+ "readOnly": {
+ "description": "if the property is marked read-only (ie, not intended to be sent to the service)",
+ "type": "boolean"
+ },
+ "serializedName": {
+ "description": "the wire name of this property",
+ "type": "string"
+ },
+ "flattenedNames": {
+ "description": "when a property is flattened, the property will be the set of serialized names to get to that target property.\n\nIf flattenedName is present, then this property is a flattened property.\n\n(ie, ['properties','name'] )",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "isDiscriminator": {
+ "description": "if this property is used as a discriminator for a polymorphic type",
+ "type": "boolean"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "schema",
+ "serializedName"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Value"
+ }
+ ]
+ },
+ "ImplementationLocation": {
+ "enum": [
+ "Client",
+ "Context",
+ "Method"
+ ],
+ "type": "string"
+ },
+ "Parameter": {
+ "description": "a definition of an discrete input for an operation",
+ "type": "object",
+ "properties": {
+ "implementation": {
+ "$ref": "#/definitions/ImplementationLocation",
+ "description": "suggested implementation location for this parameter"
+ },
+ "flattened": {
+ "description": "When a parameter is flattened, it will be left in the list, but marked hidden (so, don't generate those!)",
+ "type": "boolean"
+ },
+ "groupedBy": {
+ "$ref": "#/definitions/Parameter",
+ "description": "When a parameter is grouped into another, this will tell where the parameter got grouped into."
+ },
+ "isPartialBody": {
+ "description": "If this parameter is only part of the body request(for multipart and form bodies.)",
+ "type": "boolean"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "schema"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Value"
+ }
+ ]
+ },
+ "VirtualParameter": {
+ "type": "object",
+ "properties": {
+ "originalParameter": {
+ "$ref": "#/definitions/Parameter",
+ "description": "the original body parameter that this parameter is in effect replacing"
+ },
+ "pathToProperty": {
+ "description": "if this parameter is for a nested property, this is the path of properties it takes to get there",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Property"
+ }
+ },
+ "targetProperty": {
+ "$ref": "#/definitions/Property",
+ "description": "the target property this virtual parameter represents"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "originalParameter",
+ "pathToProperty",
+ "protocol",
+ "schema",
+ "targetProperty"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Parameter"
+ }
+ ]
+ },
+ "Response": {
+ "description": "a response from a service.",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Metadata"
+ }
+ ]
+ },
+ "BinaryResponse": {
+ "description": "a response where the content should be treated as a binary instead of a value or object",
+ "type": "object",
+ "properties": {
+ "binary": {
+ "description": "indicates that this response is a binary stream",
+ "type": "boolean",
+ "enum": [
+ true
+ ]
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "binary",
+ "language",
+ "protocol"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Response"
+ }
+ ]
+ },
+ "SchemaResponse": {
+ "description": "a response that should be deserialized into a result of type(schema)",
+ "type": "object",
+ "properties": {
+ "schema": {
+ "$ref": "#/definitions/Schema",
+ "description": "the content returned by the service for a given operaiton"
+ },
+ "nullable": {
+ "description": "indicates whether the response can be 'null'",
+ "type": "boolean"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "schema"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Response"
+ }
+ ]
+ },
+ "Operation": {
+ "description": "represents a single callable endpoint with a discrete set of inputs, and any number of output possibilities (responses or exceptions)",
+ "type": "object",
+ "properties": {
+ "operationId": {
+ "description": "Original Operation ID if present.\nThis can be used to identify the original id of an operation before it is styled.\nTHIS IS NOT the name of the operation that should be used in the generator. Use `.language.default.name` for this",
+ "type": "string"
+ },
+ "parameters": {
+ "description": "common parameters when there are multiple requests",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Parameter"
+ }
+ },
+ "signatureParameters": {
+ "description": "a common filtered list of parameters that is (assumably) the actual method signature parameters",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Parameter"
+ }
+ },
+ "requestMediaTypes": {
+ "$ref": "#/definitions/Record",
+ "description": "Mapping of all the content types available for this operation to the coresponding request."
+ },
+ "specialHeaders": {
+ "description": "List of headers that parameters should not handle as parameters but with special logic.\nSee https://github.com/Azure/autorest/tree/main/packages/extensions/modelerfour for configuration `skip-special-headers` to exclude headers.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "requests": {
+ "description": "the different possibilities to build the request.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Request"
+ }
+ },
+ "responses": {
+ "description": "responses that indicate a successful call",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Response"
+ }
+ },
+ "exceptions": {
+ "description": "responses that indicate a failed call",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Response"
+ }
+ },
+ "profile": {
+ "$ref": "#/definitions/Record",
+ "description": "the apiVersion to use for a given profile name"
+ },
+ "summary": {
+ "description": "a short description",
+ "type": "string"
+ },
+ "apiVersions": {
+ "description": "API versions that this applies to. Undefined means all versions",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ApiVersion"
+ }
+ },
+ "deprecated": {
+ "$ref": "#/definitions/Deprecation",
+ "description": "Represent the deprecation information if api is deprecated.",
+ "default": "undefined"
+ },
+ "origin": {
+ "description": "where did this aspect come from (jsonpath or 'modelerfour:')",
+ "type": "string"
+ },
+ "externalDocs": {
+ "$ref": "#/definitions/ExternalDocumentation",
+ "description": "External Documentation Links"
+ },
+ "language": {
+ "$ref": "#/definitions/Languages",
+ "description": "per-language information for this aspect"
+ },
+ "protocol": {
+ "$ref": "#/definitions/Protocols",
+ "description": "per-protocol information for this aspect"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol"
+ ]
+ },
+ "Request": {
+ "type": "object",
+ "properties": {
+ "parameters": {
+ "description": "the parameter inputs to the operation",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Parameter"
+ }
+ },
+ "signatureParameters": {
+ "description": "a filtered list of parameters that is (assumably) the actual method signature parameters",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Parameter"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Metadata"
+ }
+ ]
+ },
+ "OperationGroup": {
+ "description": "an operation group represents a container around set of operations",
+ "type": "object",
+ "properties": {
+ "$key": {
+ "type": "string"
+ },
+ "operations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Operation"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "$key",
+ "language",
+ "operations",
+ "protocol"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Metadata"
+ }
+ ]
+ },
+ "AnySchema": {
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Schema"
+ }
+ ]
+ },
+ "AnyObjectSchema": {
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Schema"
+ }
+ ]
+ },
+ "ArraySchema": {
+ "description": "a Schema that represents and array of values",
+ "type": "object",
+ "properties": {
+ "elementType": {
+ "$ref": "#/definitions/Schema",
+ "description": "elementType of the array"
+ },
+ "maxItems": {
+ "description": "maximum number of elements in the array",
+ "type": "number"
+ },
+ "minItems": {
+ "description": "minimum number of elements in the array",
+ "type": "number"
+ },
+ "uniqueItems": {
+ "description": "if the elements in the array should be unique",
+ "type": "boolean"
+ },
+ "nullableItems": {
+ "description": "if elements in the array should be nullable",
+ "type": "boolean"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "elementType",
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/ValueSchema"
+ }
+ ]
+ },
+ "ByteArraySchema": {
+ "description": "a schema that represents a ByteArray value",
+ "type": "object",
+ "properties": {
+ "format": {
+ "description": "date-time format",
+ "enum": [
+ "base64url",
+ "byte"
+ ],
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "format",
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/ValueSchema"
+ }
+ ]
+ },
+ "BinarySchema": {
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Schema"
+ }
+ ]
+ },
+ "StringSchema": {
+ "description": "a Schema that represents a string value",
+ "type": "object",
+ "properties": {
+ "maxLength": {
+ "description": "the maximum length of the string",
+ "type": "number"
+ },
+ "minLength": {
+ "description": "the minimum length of the string",
+ "type": "number"
+ },
+ "pattern": {
+ "description": "a regular expression that the string must be validated against",
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "ODataQuerySchema": {
+ "description": "a schema that represents a ODataQuery value",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Schema"
+ }
+ ]
+ },
+ "CredentialSchema": {
+ "description": "a schema that represents a Credential value",
+ "type": "object",
+ "properties": {
+ "maxLength": {
+ "description": "the maximum length of the string",
+ "type": "number"
+ },
+ "minLength": {
+ "description": "the minimum length of the string",
+ "type": "number"
+ },
+ "pattern": {
+ "description": "a regular expression that the string must be validated against",
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "UriSchema": {
+ "description": "a schema that represents a Uri value",
+ "type": "object",
+ "properties": {
+ "maxLength": {
+ "description": "the maximum length of the string",
+ "type": "number"
+ },
+ "minLength": {
+ "description": "the minimum length of the string",
+ "type": "number"
+ },
+ "pattern": {
+ "description": "a regular expression that the string must be validated against",
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "UuidSchema": {
+ "description": "a schema that represents a Uuid value",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "ArmIdSchema": {
+ "description": "a schema that represents a Uuid value",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "ChoiceSchema": {
+ "description": "a schema that represents a choice of several values (ie, an 'enum')",
+ "type": "object",
+ "properties": {
+ "choiceType": {
+ "$ref": "#/definitions/PrimitiveSchema",
+ "description": "the primitive type for the choices"
+ },
+ "choices": {
+ "description": "the possible choices for in the set",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ChoiceValue"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "choiceType",
+ "choices",
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/ValueSchema"
+ }
+ ]
+ },
+ "ChoiceValue": {
+ "description": "an individual choice in a ChoiceSchema",
+ "type": "object",
+ "properties": {
+ "language": {
+ "$ref": "#/definitions/Languages",
+ "description": "per-language information for this value"
+ },
+ "value": {
+ "description": "the actual value",
+ "type": [
+ "string",
+ "number",
+ "boolean"
+ ]
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "value"
+ ]
+ },
+ "SealedChoiceSchema": {
+ "description": "a schema that represents a choice of several values (ie, an 'enum')",
+ "type": "object",
+ "properties": {
+ "choiceType": {
+ "$ref": "#/definitions/PrimitiveSchema",
+ "description": "the primitive type for the choices"
+ },
+ "choices": {
+ "description": "the possible choices for in the set",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ChoiceValue"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "choiceType",
+ "choices",
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/ValueSchema"
+ }
+ ]
+ },
+ "ConditionalSchema": {
+ "description": "a schema that represents a value dependent on another",
+ "type": "object",
+ "properties": {
+ "conditionalType": {
+ "$ref": "#/definitions/PrimitiveSchema",
+ "description": "the primitive type for the conditional"
+ },
+ "conditions": {
+ "description": "the possible conditinal values",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ConditionalValue"
+ }
+ },
+ "sourceValue": {
+ "$ref": "#/definitions/Value",
+ "description": "the source value that drives the target value (property or parameter)"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "conditionalType",
+ "conditions",
+ "language",
+ "protocol",
+ "sourceValue",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/ValueSchema"
+ }
+ ]
+ },
+ "ConditionalValue": {
+ "description": "an individual value in a ConditionalSchema",
+ "type": "object",
+ "properties": {
+ "language": {
+ "$ref": "#/definitions/Languages",
+ "description": "per-language information for this value"
+ },
+ "target": {
+ "description": "the actual value",
+ "type": [
+ "string",
+ "number",
+ "boolean"
+ ]
+ },
+ "source": {
+ "description": "the actual value",
+ "type": [
+ "string",
+ "number",
+ "boolean"
+ ]
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "source",
+ "target"
+ ]
+ },
+ "SealedConditionalSchema": {
+ "description": "a schema that represents a value dependent on another (not overridable)",
+ "type": "object",
+ "properties": {
+ "conditionalType": {
+ "$ref": "#/definitions/PrimitiveSchema",
+ "description": "the primitive type for the condition"
+ },
+ "conditions": {
+ "description": "the possible conditional values",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ConditionalValue"
+ }
+ },
+ "sourceValue": {
+ "$ref": "#/definitions/Value",
+ "description": "the source value that drives the target value"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "conditionalType",
+ "conditions",
+ "language",
+ "protocol",
+ "sourceValue",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/ValueSchema"
+ }
+ ]
+ },
+ "ConstantValue": {
+ "description": "a container for the actual constant value",
+ "type": "object",
+ "properties": {
+ "language": {
+ "$ref": "#/definitions/Languages",
+ "description": "per-language information for this value"
+ },
+ "value": {
+ "description": "the actual constant value to use"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "value"
+ ]
+ },
+ "ConstantSchema": {
+ "description": "a schema that represents a constant value",
+ "type": "object",
+ "properties": {
+ "valueType": {
+ "$ref": "#/definitions/Schema",
+ "description": "the schema type of the constant value (ie, StringSchema, NumberSchema, etc)"
+ },
+ "value": {
+ "$ref": "#/definitions/ConstantValue",
+ "description": "the actual constant value"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type",
+ "value",
+ "valueType"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Schema"
+ }
+ ]
+ },
+ "DictionarySchema": {
+ "description": "a schema that represents a key-value collection",
+ "type": "object",
+ "properties": {
+ "elementType": {
+ "$ref": "#/definitions/Schema",
+ "description": "the element type of the dictionary. (Keys are always strings)"
+ },
+ "nullableItems": {
+ "description": "if elements in the dictionary should be nullable",
+ "type": "boolean"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "elementType",
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/ComplexSchema"
+ }
+ ]
+ },
+ "FlagValue": {
+ "type": "object",
+ "properties": {
+ "language": {
+ "$ref": "#/definitions/Languages",
+ "description": "per-language information for this value"
+ },
+ "value": {
+ "type": "number"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "value"
+ ]
+ },
+ "FlagSchema": {
+ "type": "object",
+ "properties": {
+ "choices": {
+ "description": "the possible choices for in the set",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/FlagValue"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "choices",
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/ValueSchema"
+ }
+ ]
+ },
+ "NumberSchema": {
+ "description": "a Schema that represents a Number value",
+ "type": "object",
+ "properties": {
+ "precision": {
+ "description": "precision (# of bits?) of the number",
+ "type": "number"
+ },
+ "multipleOf": {
+ "description": "if present, the number must be an exact multiple of this value",
+ "type": "number"
+ },
+ "maximum": {
+ "description": "if present, the value must be lower than or equal to this (unless exclusiveMaximum is true)",
+ "type": "number"
+ },
+ "exclusiveMaximum": {
+ "description": "if present, the value must be lower than maximum",
+ "type": "boolean"
+ },
+ "minimum": {
+ "description": "if present, the value must be highter than or equal to this (unless exclusiveMinimum is true)",
+ "type": "number"
+ },
+ "exclusiveMinimum": {
+ "description": "if present, the value must be higher than minimum",
+ "type": "boolean"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "precision",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "SchemaContext": {
+ "enum": [
+ "exception",
+ "input",
+ "output"
+ ],
+ "type": "string"
+ },
+ "SchemaUsage": {
+ "type": "object",
+ "properties": {
+ "usage": {
+ "description": "contexts in which the schema is used",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/SchemaContext"
+ }
+ },
+ "serializationFormats": {
+ "description": "Known media types in which this schema can be serialized",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/KnownMediaType"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "Relations": {
+ "type": "object",
+ "properties": {
+ "immediate": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ComplexSchema"
+ }
+ },
+ "all": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ComplexSchema"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "all",
+ "immediate"
+ ]
+ },
+ "Discriminator": {
+ "type": "object",
+ "properties": {
+ "property": {
+ "$ref": "#/definitions/Property"
+ },
+ "immediate": {
+ "$ref": "#/definitions/Record"
+ },
+ "all": {
+ "$ref": "#/definitions/Record"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "all",
+ "immediate",
+ "property"
+ ]
+ },
+ "GroupProperty": {
+ "type": "object",
+ "properties": {
+ "originalParameter": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Parameter"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "originalParameter",
+ "protocol",
+ "schema",
+ "serializedName"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Property"
+ }
+ ]
+ },
+ "GroupSchema": {
+ "type": "object",
+ "properties": {
+ "properties": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/GroupProperty"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Schema"
+ },
+ {
+ "$ref": "#/definitions/SchemaUsage"
+ }
+ ]
+ },
+ "ObjectSchema": {
+ "description": "a schema that represents a type with child properties.",
+ "type": "object",
+ "properties": {
+ "discriminator": {
+ "$ref": "#/definitions/Discriminator",
+ "description": "the property of the polymorphic descriminator for this type, if there is one"
+ },
+ "properties": {
+ "description": "the collection of properties that are in this object",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Property"
+ }
+ },
+ "maxProperties": {
+ "description": "maximum number of properties permitted",
+ "type": "number"
+ },
+ "minProperties": {
+ "description": "minimum number of properties permitted",
+ "type": "number"
+ },
+ "parents": {
+ "$ref": "#/definitions/Relations"
+ },
+ "children": {
+ "$ref": "#/definitions/Relations"
+ },
+ "discriminatorValue": {
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/ComplexSchema"
+ },
+ {
+ "$ref": "#/definitions/SchemaUsage"
+ }
+ ]
+ },
+ "BooleanSchema": {
+ "description": "a schema that represents a boolean value",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "CharSchema": {
+ "description": "a schema that represents a Char value",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "OrSchema": {
+ "description": "an OR relationship between several schemas",
+ "type": "object",
+ "properties": {
+ "anyOf": {
+ "description": "the set of schemas that this schema is composed of. Every schema is optional",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ComplexSchema"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "anyOf",
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/ComplexSchema"
+ }
+ ]
+ },
+ "XorSchema": {
+ "description": "an XOR relationship between several schemas",
+ "type": "object",
+ "properties": {
+ "oneOf": {
+ "description": "the set of schemas that this must be one and only one of.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Schema"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "oneOf",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Schema"
+ }
+ ]
+ },
+ "NotSchema": {
+ "description": "a NOT relationship between schemas",
+ "type": "object",
+ "properties": {
+ "not": {
+ "$ref": "#/definitions/Schema",
+ "description": "the schema that this may not be."
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "not",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Schema"
+ }
+ ]
+ },
+ "DurationSchema": {
+ "description": "a schema that represents a Duration value",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "DateTimeSchema": {
+ "description": "a schema that represents a DateTime value",
+ "type": "object",
+ "properties": {
+ "format": {
+ "description": "date-time format",
+ "enum": [
+ "date-time",
+ "date-time-rfc1123"
+ ],
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "format",
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "DateSchema": {
+ "description": "a schema that represents a Date value",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "TimeSchema": {
+ "description": "a schema that represents a Date value",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "UnixTimeSchema": {
+ "description": "a schema that represents a UnixTime value",
+ "type": "object",
+ "properties": {},
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/PrimitiveSchema"
+ }
+ ]
+ },
+ "Schemas": {
+ "description": "the full set of schemas for a given service, categorized into convenient collections",
+ "type": "object",
+ "properties": {
+ "arrays": {
+ "description": "a collection of items",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArraySchema"
+ }
+ },
+ "dictionaries": {
+ "description": "an associative array (ie, dictionary, hashtable, etc)",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/DictionarySchema"
+ }
+ },
+ "booleans": {
+ "description": "a true or false value",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/BooleanSchema"
+ }
+ },
+ "numbers": {
+ "description": "a number value",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/NumberSchema"
+ }
+ },
+ "objects": {
+ "description": "an object of some type",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ObjectSchema"
+ }
+ },
+ "strings": {
+ "description": "a string of characters",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/StringSchema"
+ }
+ },
+ "unixtimes": {
+ "description": "UnixTime",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/UnixTimeSchema"
+ }
+ },
+ "byteArrays": {
+ "description": "ByteArray -- an array of bytes",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ByteArraySchema"
+ }
+ },
+ "streams": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Schema"
+ }
+ },
+ "chars": {
+ "description": "a single character",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/CharSchema"
+ }
+ },
+ "dates": {
+ "description": "a Date",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/DateSchema"
+ }
+ },
+ "times": {
+ "description": "a time",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/TimeSchema"
+ }
+ },
+ "dateTimes": {
+ "description": "a DateTime",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/DateTimeSchema"
+ }
+ },
+ "durations": {
+ "description": "a Duration",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/DurationSchema"
+ }
+ },
+ "uuids": {
+ "description": "a universally unique identifier",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/UuidSchema"
+ }
+ },
+ "uris": {
+ "description": "an URI of some kind",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/UriSchema"
+ }
+ },
+ "armIds": {
+ "description": "an URI of some kind",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArmIdSchema"
+ }
+ },
+ "credentials": {
+ "description": "a password or credential",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/CredentialSchema"
+ }
+ },
+ "odataQueries": {
+ "description": "OData Query",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ODataQuerySchema"
+ }
+ },
+ "choices": {
+ "description": "- this is essentially can be thought of as an 'enum'\nthat is a choice between one of several items, but an unspecified value is permitted.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ChoiceSchema"
+ }
+ },
+ "sealedChoices": {
+ "description": "- this is essentially can be thought of as an 'enum'\nthat is a choice between one of several items, but an unknown value is not allowed.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/SealedChoiceSchema"
+ }
+ },
+ "conditionals": {
+ "description": "ie, when 'profile' is 'production', use '2018-01-01' for apiversion",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ConditionalSchema"
+ }
+ },
+ "sealedConditionals": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/SealedConditionalSchema"
+ }
+ },
+ "flags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/FlagSchema"
+ }
+ },
+ "constants": {
+ "description": "a constant value",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ConstantSchema"
+ }
+ },
+ "ors": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/OrSchema"
+ }
+ },
+ "xors": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/XorSchema"
+ }
+ },
+ "binaries": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/BinarySchema"
+ }
+ },
+ "unknowns": {
+ "description": "it's possible that we just may make this an error\nin representation.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Schema"
+ }
+ },
+ "groups": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/GroupSchema"
+ }
+ },
+ "any": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/AnySchema"
+ }
+ },
+ "anyObjects": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/AnyObjectSchema"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "Security": {
+ "description": "The security information for the API surface",
+ "type": "object",
+ "properties": {
+ "authenticationRequired": {
+ "description": "indicates that the API surface requires authentication",
+ "type": "boolean"
+ },
+ "schemes": {
+ "items": {
+ "type": "SecuritySchemeFull"
+ },
+ "type": "array"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "authenticationRequired",
+ "schemes"
+ ]
+ },
+ "SecurityScheme": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "type"
+ ]
+ },
+ "OAuth2SecurityScheme": {
+ "type": "object",
+ "properties": {
+ "scopes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "scopes",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/SecurityScheme"
+ }
+ ]
+ },
+ "KeySecurityScheme": {
+ "type": "object",
+ "properties": {
+ "in": {
+ "type": "string",
+ "enum": [
+ "header"
+ ]
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "in",
+ "name",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/SecurityScheme"
+ }
+ ]
+ },
+ "ValueOrFactory": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/ApiVersion"
+ },
+ {
+ "type": "object",
+ "defaultProperties": [],
+ "additionalProperties": false
+ }
+ ]
+ },
+ "Example": {
+ "description": "example data [UNFINISHED]",
+ "type": "object",
+ "properties": {
+ "summary": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "value": {},
+ "externalValue": {
+ "description": "an URI",
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "AADTokenSecurityScheme": {
+ "type": "object",
+ "properties": {
+ "scopes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "scopes",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/SecurityScheme"
+ }
+ ]
+ },
+ "AzureKeySecurityScheme": {
+ "type": "object",
+ "properties": {
+ "headerName": {
+ "type": "string"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "headerName",
+ "type"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/SecurityScheme"
+ }
+ ]
+ },
+ "HttpMethod": {
+ "description": "standard HTTP protocol methods",
+ "enum": [
+ "delete",
+ "get",
+ "head",
+ "options",
+ "patch",
+ "post",
+ "put",
+ "trace"
+ ],
+ "type": "string"
+ },
+ "ParameterLocation": {
+ "description": "the location that this parameter is placed in the http request",
+ "enum": [
+ "body",
+ "cookie",
+ "header",
+ "none",
+ "path",
+ "query",
+ "uri",
+ "virtual"
+ ],
+ "type": "string"
+ },
+ "Scheme": {
+ "type": "string",
+ "enum": [
+ "bearer"
+ ]
+ },
+ "SecurityType": {
+ "enum": [
+ "apiKey",
+ "http",
+ "oauth2",
+ "openIdConnect"
+ ],
+ "type": "string"
+ },
+ "AuthorizationCodeOAuthFlow": {
+ "type": "object",
+ "properties": {
+ "authorizationUrl": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "tokenUrl": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "refreshUrl": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "scopes": {
+ "$ref": "#/definitions/Record"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "authorizationUrl",
+ "scopes",
+ "tokenUrl"
+ ]
+ },
+ "BearerHTTPSecurityScheme": {
+ "type": "object",
+ "properties": {
+ "scheme": {
+ "type": "string",
+ "enum": [
+ "bearer"
+ ]
+ },
+ "bearerFormat": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "http"
+ ]
+ },
+ "description": {
+ "type": "string"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "scheme",
+ "type"
+ ]
+ },
+ "ClientCredentialsFlow": {
+ "type": "object",
+ "properties": {
+ "tokenUrl": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "refreshUrl": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "scopes": {
+ "$ref": "#/definitions/Record"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "scopes",
+ "tokenUrl"
+ ]
+ },
+ "ImplicitOAuthFlow": {
+ "type": "object",
+ "properties": {
+ "authorizationUrl": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "refreshUrl": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "scopes": {
+ "$ref": "#/definitions/Record"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "authorizationUrl",
+ "scopes"
+ ]
+ },
+ "NonBearerHTTPSecurityScheme": {
+ "type": "object",
+ "properties": {
+ "scheme": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "http"
+ ]
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "scheme",
+ "type"
+ ]
+ },
+ "OAuthFlows": {
+ "type": "object",
+ "properties": {
+ "implicit": {
+ "$ref": "#/definitions/ImplicitOAuthFlow"
+ },
+ "password": {
+ "$ref": "#/definitions/PasswordOAuthFlow"
+ },
+ "clientCredentials": {
+ "$ref": "#/definitions/ClientCredentialsFlow"
+ },
+ "authorizationCode": {
+ "$ref": "#/definitions/AuthorizationCodeOAuthFlow"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "HTTPSecurityScheme": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/BearerHTTPSecurityScheme"
+ },
+ {
+ "$ref": "#/definitions/NonBearerHTTPSecurityScheme"
+ }
+ ]
+ },
+ "APIKeySecurityScheme": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "apiKey"
+ ]
+ },
+ "name": {
+ "type": "string"
+ },
+ "in": {
+ "$ref": "#/definitions/ParameterLocation"
+ },
+ "description": {
+ "type": "string"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "in",
+ "name",
+ "type"
+ ]
+ },
+ "OpenIdConnectSecurityScheme": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "openIdConnect"
+ ]
+ },
+ "openIdConnectUrl": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "openIdConnectUrl",
+ "type"
+ ]
+ },
+ "PasswordOAuthFlow": {
+ "type": "object",
+ "properties": {
+ "tokenUrl": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "refreshUrl": {
+ "description": "an URI",
+ "type": "string"
+ },
+ "scopes": {
+ "$ref": "#/definitions/Record"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "scopes",
+ "tokenUrl"
+ ]
+ },
+ "SecurityRequirement": {
+ "description": "common ways of serializing simple parameters",
+ "type": "object",
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "SerializationStyle": {
+ "description": "The Serialization Style used for the parameter.\n\nDescribes how the parameter value will be serialized depending on the type of the parameter value.",
+ "enum": [
+ "binary",
+ "deepObject",
+ "form",
+ "json",
+ "label",
+ "matrix",
+ "pipeDelimited",
+ "simple",
+ "spaceDelimited",
+ "tabDelimited",
+ "xml"
+ ],
+ "type": "string"
+ },
+ "QueryEncodingStyle": {
+ "enum": [
+ "deepObject",
+ "form",
+ "pipeDelimited",
+ "spaceDelimited"
+ ],
+ "type": "string"
+ },
+ "PathEncodingStyle": {
+ "enum": [
+ "label",
+ "matrix",
+ "simple"
+ ],
+ "type": "string"
+ },
+ "Default": {
+ "description": "A catch-all for all un-handled response codes.",
+ "type": "string",
+ "enum": [
+ "default"
+ ]
+ },
+ "StatusCode": {
+ "enum": [
+ 100,
+ 101,
+ 102,
+ 103,
+ 200,
+ 201,
+ 202,
+ 203,
+ 204,
+ 205,
+ 206,
+ 207,
+ 208,
+ 226,
+ 300,
+ 301,
+ 302,
+ 303,
+ 304,
+ 305,
+ 306,
+ 307,
+ 308,
+ 400,
+ 401,
+ 402,
+ 403,
+ 404,
+ 405,
+ 406,
+ 407,
+ 408,
+ 409,
+ 410,
+ 411,
+ 412,
+ 413,
+ 414,
+ 415,
+ 416,
+ 417,
+ 418,
+ 421,
+ 422,
+ 423,
+ 424,
+ 425,
+ 426,
+ 428,
+ 429,
+ 431,
+ 451,
+ 500,
+ 501,
+ 502,
+ 503,
+ 504,
+ 505,
+ 506,
+ 507,
+ 508,
+ 510,
+ 511,
+ "default"
+ ]
+ },
+ "HttpParameter": {
+ "description": "extended metadata for HTTP operation parameters",
+ "type": "object",
+ "properties": {
+ "in": {
+ "description": "the location that this parameter is placed in the http request",
+ "enum": [
+ "body",
+ "cookie",
+ "header",
+ "none",
+ "path",
+ "query",
+ "uri",
+ "virtual"
+ ],
+ "type": "string"
+ },
+ "style": {
+ "$ref": "#/definitions/SerializationStyle",
+ "description": "the Serialization Style used for the parameter."
+ },
+ "explode": {
+ "description": "when set, 'form' style parameters generate separate parameters for each value of an array.",
+ "type": "boolean"
+ },
+ "skipUriEncoding": {
+ "description": "when set, this indicates that the content of the parameter should not be subject to URI encoding rules.",
+ "type": "boolean"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "in"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Protocol"
+ }
+ ]
+ },
+ "HttpRequest": {
+ "description": "HTTP operation protocol data",
+ "type": "object",
+ "properties": {
+ "path": {
+ "description": "A relative path to an individual endpoint.\n\nThe field name MUST begin with a slash.\nThe path is appended (no relative URL resolution) to the expanded URL from the Server Object's url field in order to construct the full URL.\nPath templating is allowed.\n\nWhen matching URLs, concrete (non-templated) paths would be matched before their templated counterparts.",
+ "type": "string"
+ },
+ "uri": {
+ "description": "the base URI template for the operation. This will be a template that has Uri parameters to craft the base url to use.",
+ "type": "string"
+ },
+ "method": {
+ "$ref": "#/definitions/HttpMethod",
+ "description": "the HTTP Method used to process this operation"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "method",
+ "path",
+ "uri"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Protocol"
+ }
+ ]
+ },
+ "HttpWithBodyRequest": {
+ "type": "object",
+ "properties": {
+ "knownMediaType": {
+ "$ref": "#/definitions/KnownMediaType",
+ "description": "a normalized value for the media type (ie, distills down to a well-known moniker (ie, 'json'))"
+ },
+ "mediaTypes": {
+ "description": "must contain at least one media type to send for the body",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "knownMediaType",
+ "mediaTypes",
+ "method",
+ "path",
+ "uri"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/HttpRequest"
+ }
+ ]
+ },
+ "HttpBinaryRequest": {
+ "type": "object",
+ "properties": {
+ "binary": {
+ "type": "boolean",
+ "enum": [
+ true
+ ]
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "binary",
+ "knownMediaType",
+ "mediaTypes",
+ "method",
+ "path",
+ "uri"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/HttpWithBodyRequest"
+ }
+ ]
+ },
+ "HttpMultipartRequest": {
+ "type": "object",
+ "properties": {
+ "multipart": {
+ "description": "indicates that the HTTP Request should be a multipart request\n\nie, that it has multiple requests in a single request.",
+ "type": "boolean",
+ "enum": [
+ true
+ ]
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "knownMediaType",
+ "mediaTypes",
+ "method",
+ "multipart",
+ "path",
+ "uri"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/HttpWithBodyRequest"
+ }
+ ]
+ },
+ "HttpHeader": {
+ "type": "object",
+ "properties": {
+ "header": {
+ "type": "string"
+ },
+ "schema": {
+ "$ref": "#/definitions/Schema"
+ },
+ "language": {
+ "$ref": "#/definitions/Languages"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "header",
+ "language",
+ "schema"
+ ]
+ },
+ "HttpResponse": {
+ "type": "object",
+ "properties": {
+ "statusCodes": {
+ "description": "the possible HTTP status codes that this response MUST match one of.",
+ "type": "array",
+ "items": {
+ "enum": [
+ 100,
+ 101,
+ 102,
+ 103,
+ 200,
+ 201,
+ 202,
+ 203,
+ 204,
+ 205,
+ 206,
+ 207,
+ 208,
+ 226,
+ 300,
+ 301,
+ 302,
+ 303,
+ 304,
+ 305,
+ 306,
+ 307,
+ 308,
+ 400,
+ 401,
+ 402,
+ 403,
+ 404,
+ 405,
+ 406,
+ 407,
+ 408,
+ 409,
+ 410,
+ 411,
+ 412,
+ 413,
+ 414,
+ 415,
+ 416,
+ 417,
+ 418,
+ 421,
+ 422,
+ 423,
+ 424,
+ 425,
+ 426,
+ 428,
+ 429,
+ 431,
+ 451,
+ 500,
+ 501,
+ 502,
+ 503,
+ 504,
+ 505,
+ 506,
+ 507,
+ 508,
+ 510,
+ 511,
+ "default"
+ ]
+ }
+ },
+ "knownMediaType": {
+ "$ref": "#/definitions/KnownMediaType",
+ "description": "canonical response type (ie, 'json')."
+ },
+ "mediaTypes": {
+ "description": "The possible media types that this response MUST match one of.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "headers": {
+ "description": "content returned by the service in the HTTP headers",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/HttpHeader"
+ }
+ },
+ "headerGroups": {
+ "description": "sets of HTTP headers grouped together into a single schema",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/GroupSchema"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "statusCodes"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/Protocol"
+ }
+ ]
+ },
+ "HttpBinaryResponse": {
+ "type": "object",
+ "properties": {
+ "binary": {
+ "description": "binary responses",
+ "type": "boolean",
+ "enum": [
+ true
+ ]
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "binary",
+ "statusCodes"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/HttpResponse"
+ }
+ ]
+ },
+ "HttpModel": {
+ "description": "code model metadata for HTTP protocol",
+ "type": "object",
+ "properties": {
+ "security": {
+ "description": "a collection of security requirements for the service",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/SecurityRequirement"
+ }
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "allOf": [
+ {
+ "$ref": "#/definitions/Protocol"
+ }
+ ]
+ },
+ "Record": {
+ "type": "object",
+ "defaultProperties": [],
+ "additionalProperties": {
+ "type": "object"
+ }
+ },
+ "Record": {
+ "type": "object",
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "Record": {
+ "type": "object",
+ "defaultProperties": [],
+ "additionalProperties": false
+ },
+ "ConstantType": {
+ "type": "object",
+ "properties": {
+ "language": {
+ "$ref": "#/definitions/Languages",
+ "description": "per-language information for Schema"
+ },
+ "type": {
+ "$ref": "#/definitions/AllSchemaTypes",
+ "description": "the schema type"
+ },
+ "summary": {
+ "description": "a short description",
+ "type": "string"
+ },
+ "example": {
+ "description": "example information"
+ },
+ "defaultValue": {
+ "description": "If the value isn't sent on the wire, the service will assume this"
+ },
+ "serialization": {
+ "$ref": "#/definitions/SerializationFormats",
+ "description": "per-serialization information for this Schema"
+ },
+ "apiVersions": {
+ "description": "API versions that this applies to. Undefined means all versions",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ApiVersion"
+ }
+ },
+ "deprecated": {
+ "$ref": "#/definitions/Deprecation",
+ "description": "Represent the deprecation information if api is deprecated.",
+ "default": "undefined"
+ },
+ "origin": {
+ "description": "where did this aspect come from (jsonpath or 'modelerfour:')",
+ "type": "string"
+ },
+ "externalDocs": {
+ "$ref": "#/definitions/ExternalDocumentation",
+ "description": "External Documentation Links"
+ },
+ "protocol": {
+ "$ref": "#/definitions/Protocols",
+ "description": "per-protocol information for this aspect"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "language",
+ "protocol",
+ "type"
+ ]
+ },
+ "Record": {
+ "type": "object",
+ "defaultProperties": [],
+ "additionalProperties": {
+ "$ref": "#/definitions/ComplexSchema"
+ }
+ },
+ "Record": {
+ "type": "object",
+ "defaultProperties": [],
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "KnownMediaType": {
+ "enum": [
+ "binary",
+ "form",
+ "json",
+ "multipart",
+ "text",
+ "unknown",
+ "xml"
+ ],
+ "type": "string"
+ }
+ },
+ "description": "the model that contains all the information required to generate a service api",
+ "type": "object",
+ "properties": {
+ "info": {
+ "$ref": "#/definitions/Info",
+ "description": "Code model information"
+ },
+ "schemas": {
+ "$ref": "#/definitions/Schemas",
+ "description": "All schemas for the model"
+ },
+ "operationGroups": {
+ "description": "All operations",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/OperationGroup"
+ }
+ },
+ "globalParameters": {
+ "description": "all global parameters (ie, ImplementationLocation = client )",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Parameter"
+ }
+ },
+ "security": {
+ "$ref": "#/definitions/Security"
+ },
+ "language": {
+ "$ref": "#/definitions/Languages",
+ "description": "per-language information for this aspect"
+ },
+ "protocol": {
+ "$ref": "#/definitions/Protocols",
+ "description": "per-protocol information for this aspect"
+ },
+ "extensions": {
+ "$ref": "#/definitions/Record",
+ "description": "additional metadata extensions dictionary"
+ }
+ },
+ "defaultProperties": [],
+ "additionalProperties": false,
+ "required": [
+ "info",
+ "language",
+ "operationGroups",
+ "protocol",
+ "schemas",
+ "security"
+ ],
+ "title": "CodeModel"
+}
\ No newline at end of file
diff --git a/logger/autorest.csharp/build/CodeGeneration.targets b/logger/autorest.csharp/build/CodeGeneration.targets
new file mode 100644
index 0000000..cff7487
--- /dev/null
+++ b/logger/autorest.csharp/build/CodeGeneration.targets
@@ -0,0 +1,99 @@
+
+
+
+ pwsh
+ <_TypeSpecProjectSyncAndGenerateCommand>npx --no-install --package=@azure-tools/typespec-client-generator-cli --yes tsp-client update --no-prompt --output-dir $(MSBuildProjectDirectory)/../
+ <_TypeSpecProjectGenerateCommand>npx --no-install --package=@azure-tools/typespec-client-generator-cli --yes tsp-client generate --no-prompt --output-dir $(MSBuildProjectDirectory)/../
+ <_DefaultInputName Condition="Exists('$(MSBuildProjectDirectory)/autorest.md')">$(MSBuildProjectDirectory)/autorest.md
+ $(MSBuildProjectDirectory)/../tsp-location.yaml
+ $(_DefaultInputName)
+ $(MSBuildProjectDirectory)/autorest.tests.md
+ $(MSBuildThisFileDirectory)../tools/autorest/entrypoints/app.js
+
+
+
+
+ true
+ $(MSBuildThisFileDirectory)../content/Azure.Core.Shared/
+ $(MSBuildThisFileDirectory)../content/Generator.Shared/
+ $(MSBuildThisFileDirectory)../content/Management.Shared/
+
+ <_GenerateCode Condition="'$(AutoRestInput)' != '' OR '$(TypeSpecInput)' != ''">true
+ true
+ <_AutoRestCommand>npx autorest@$(AutoRestVersion) --max-memory-size=8192 --skip-csproj --skip-upgrade-check --version=$(AutoRestCoreVersion) $(AutoRestInput) $(AutoRestAdditionalParameters) --use=$(MSBuildThisFileDirectory)../tools/net8.0/any/ --clear-output-folder=true --shared-source-folders="$(AzureCoreSharedCodeDirectory);$(AutoRestSharedCodeDirectory)"
+ <_AutoRestCommand Condition="'$(UseDefaultNamespaceAndOutputFolder)' == 'true'">$(_AutoRestCommand) --output-folder=$(MSBuildProjectDirectory)/Generated --namespace=$(RootNamespace)
+ <_SaveInputs Condition="'$(SaveInputs)' == 'true'">--save-inputs
+
+ $(TypespecAdditionalOptions)%3Bgenerate-test-project=true
+ generate-test-project=true
+ <_TypespecAdditionalOptions Condition="'$(TypespecAdditionalOptions)' != ''">--emitter-options "$(TypespecAdditionalOptions)"
+ <_LocalSpecRepo Condition="'$(LocalSpecRepo)' != ''">--local-spec-repo $(LocalSpecRepo)
+ <_Debug Condition="'$(Debug)' == 'true'">"--debug"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(NoWarn);CA1812
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/IPluginCommunication.cs b/logger/autorest.csharp/common/AutoRest/Communication/IPluginCommunication.cs
new file mode 100644
index 0000000..1084679
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/IPluginCommunication.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+using AutoRest.CSharp.AutoRest.Communication.Serialization.Models;
+
+namespace AutoRest.CSharp.AutoRest.Communication
+{
+ internal interface IPluginCommunication
+ {
+ string PluginName { get; }
+ Task ReadFile(string filename);
+ Task GetValue(string key);
+ Task ListInputs(string? artifactType = null);
+ Task WriteFile(string filename, string content, string artifactType, RawSourceMap? sourceMap = null);
+ Task Fatal(string text);
+ Task Warning(string text);
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/JsonRpcCommunication.cs b/logger/autorest.csharp/common/AutoRest/Communication/JsonRpcCommunication.cs
new file mode 100644
index 0000000..eab44ed
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/JsonRpcCommunication.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Threading.Tasks;
+using AutoRest.CSharp.AutoRest.Communication.MessageHandling;
+using AutoRest.CSharp.AutoRest.Communication.Serialization;
+using AutoRest.CSharp.AutoRest.Communication.Serialization.Models;
+
+namespace AutoRest.CSharp.AutoRest.Communication
+{
+ internal class JsonRpcCommunication : IPluginCommunication
+ {
+ private readonly JsonRpcConnection _connection;
+ private readonly string _sessionId;
+ public string PluginName { get; }
+
+ public JsonRpcCommunication(JsonRpcConnection connection, string pluginName, string sessionId)
+ {
+ _connection = connection;
+ PluginName = pluginName;
+ _sessionId = sessionId;
+ }
+
+ // Basic Interfaces
+ public Task ReadFile(string filename) => ProcessRequest(requestId => OutgoingMessageSerializer.ReadFile(requestId, _sessionId, filename));
+ public Task GetValue(string key) => ProcessRequest(requestId => OutgoingMessageSerializer.GetValue(requestId, _sessionId, key));
+ public Task ListInputs(string? artifactType = null) => ProcessRequest(requestId => OutgoingMessageSerializer.ListInputs(requestId, _sessionId, artifactType));
+ public Task ProtectFiles(string path) => ProcessRequest(requestId => OutgoingMessageSerializer.ProtectFiles(requestId, _sessionId, path));
+ public Task Message(IMessage message) => _connection.Notification(OutgoingMessageSerializer.Message(_sessionId, message));
+
+ public Task WriteFile(string filename, string content, string artifactType, RawSourceMap? sourceMap = null) =>
+ _connection.Notification(OutgoingMessageSerializer.WriteFile(_sessionId, filename, content, artifactType, sourceMap));
+ public Task WriteFile(string filename, string content, string artifactType, Mapping[] sourceMap) =>
+ _connection.Notification(OutgoingMessageSerializer.WriteFile(_sessionId, filename, content, artifactType, sourceMap));
+
+ public Task Fatal(string text)
+ {
+ return Message(text, Channel.Fatal);
+ }
+
+ public Task Warning(string text)
+ {
+ return Message(text, Channel.Warning);
+ }
+
+ // Convenience Interfaces
+ public Task Message(string text, Channel channel = Channel.Warning) => Message(new Message { Channel = channel, Text = text });
+
+ private Task ProcessRequest(Func requestMethod)
+ {
+ var requestId = Guid.NewGuid().ToString();
+ return _connection.Request(requestId, requestMethod(requestId));
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/IncomingMessageHandler.cs b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/IncomingMessageHandler.cs
new file mode 100644
index 0000000..a4c2a24
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/IncomingMessageHandler.cs
@@ -0,0 +1,83 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Text.Json;
+using AutoRest.CSharp.AutoRest.Communication.MessageHandling.Models;
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.MessageHandling
+{
+ internal delegate void IncomingRequestProcess(IncomingRequest request);
+ internal delegate void IncomingResponseProcess(IncomingResponse request);
+
+ internal class IncomingMessageHandler
+ {
+ private readonly PeekableBinaryStream _stream;
+ private readonly IncomingRequestProcess _requestProcess;
+ private readonly IncomingResponseProcess _responseProcess;
+
+ public IncomingMessageHandler(PeekableBinaryStream stream, IncomingRequestProcess requestProcess, IncomingResponseProcess responseProcess)
+ {
+ _stream = stream;
+ _requestProcess = requestProcess;
+ _responseProcess = responseProcess;
+ }
+
+ public bool ProcessStream()
+ {
+ var currentByte = _stream.CurrentByte;
+ if (currentByte == null) return false;
+
+ if (currentByte.IsJsonBlock())
+ {
+ ProcessMessage(_stream.ReadJson());
+ return true;
+ }
+
+ return ProcessHeaders();
+ }
+
+ private bool ProcessHeaders()
+ {
+ var headers = _stream.ReadAllAsciiLines(l => !l.IsNullOrWhiteSpace()).Select(l =>
+ {
+ var parts = l!.Split(":", 2).Select(p => p.Trim()).ToArray();
+ return (Key: parts[0], Value: parts[1]);
+ }).ToDictionary(h => h.Key, h => h.Value);
+
+ // After the headers are read, the next byte should be the content block.
+ if (_stream.CurrentByte.IsJsonBlock() && headers.TryGetValue("Content-Length", out var value) && Int32.TryParse(value, out var contentLength))
+ {
+ ProcessMessage(_stream.ReadJson(contentLength));
+ return true;
+ }
+ return false;
+ }
+
+ // Determines if the incoming message is a request or a response.
+ private void ProcessMessage(JsonElement? element)
+ {
+ if (element == null || element.Value.ValueKind != JsonValueKind.Object) return;
+
+ var properties = element.Value.EnumerateObject().Select(p => (JsonProperty?)p).ToArray();
+ var id = properties.GetPropertyOrNull("id")?.Value.ToString();
+ var method = properties.GetPropertyOrNull("method")?.Value.GetString();
+ if (!method.IsNullOrEmpty())
+ {
+ var parameters = properties.GetPropertyOrNull("params")?.Value;
+ var request = new IncomingRequest { Id = id, Method = method, Params = parameters };
+ _requestProcess(request);
+ return;
+ }
+
+ var result = properties.GetPropertyOrNull("result")?.Value.GetRawText();
+ if (!result.IsNullOrEmpty())
+ {
+ var response = new IncomingResponse { Id = id, Result = result };
+ _responseProcess(response);
+ }
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/JsonRpcConnection.cs b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/JsonRpcConnection.cs
new file mode 100644
index 0000000..55aea45
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/JsonRpcConnection.cs
@@ -0,0 +1,102 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using AutoRest.CSharp.AutoRest.Communication.MessageHandling.Models;
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.MessageHandling
+{
+ internal delegate string IncomingRequestAction(JsonRpcConnection connection, IncomingRequest request);
+
+#pragma warning disable IDE0069 // Disposable fields should be disposed
+ internal sealed class JsonRpcConnection : IDisposable
+ {
+ private readonly Stream _outputStream;
+ private readonly PeekableBinaryStream _inputStream;
+ private readonly Task _listener;
+
+ public CancellationTokenSource CancellationTokenSource { get; private set; } = new CancellationTokenSource();
+ private readonly CancellationToken _cancellationToken;
+
+ private readonly ConcurrentDictionary> _responses = new ConcurrentDictionary>();
+ private readonly Dictionary _incomingRequestActions;
+ private readonly IncomingMessageHandler _incomingMessageHandler;
+ private readonly OutgoingMessageHandler _outgoingMessageHandler;
+
+ public JsonRpcConnection(Stream inputStream, Stream outputStream, Dictionary? incomingRequestActions = null)
+ {
+ _cancellationToken = CancellationTokenSource.Token;
+ _inputStream = new PeekableBinaryStream(inputStream);
+ _outputStream = outputStream;
+ _incomingRequestActions = incomingRequestActions ?? new Dictionary();
+ _incomingMessageHandler = new IncomingMessageHandler(_inputStream, HandleIncomingRequest, HandleIncomingResponse);
+ _outgoingMessageHandler = new OutgoingMessageHandler(_outputStream, _cancellationToken);
+ _listener = Task.Factory.StartNew(Listen).Unwrap();
+ }
+
+ public void Start() => _listener.GetAwaiter().GetResult();
+
+ private Task Listen()
+ {
+ bool IsAlive() => !_cancellationToken.IsCancellationRequested;
+ while (IsAlive() && _incomingMessageHandler.ProcessStream()) { }
+ return Task.FromResult(false);
+ }
+
+ private void HandleIncomingRequest(IncomingRequest request)
+ {
+ Task.Factory.StartNew(() =>
+ {
+ if (_incomingRequestActions.TryGetValue(request.Method ?? String.Empty, out var requestAction))
+ {
+ var result = requestAction(this, request);
+ if (!request.Id.IsNullOrEmpty())
+ {
+ _outgoingMessageHandler.Respond(request.Id!, result).GetAwaiter().GetResult();
+ }
+ }
+ }, _cancellationToken);
+ }
+
+ private void HandleIncomingResponse(IncomingResponse response)
+ {
+ Task.Factory.StartNew(() =>
+ {
+ if (!response.Id.IsNullOrEmpty())
+ {
+ _responses.Remove(response.Id!, out var responseTask);
+ responseTask?.TrySetResult(response.Result ?? String.Empty);
+ }
+ }, _cancellationToken);
+ }
+
+ public async Task Notification(string json) => await _outgoingMessageHandler.Send(json).ConfigureAwait(false);
+
+ public async Task Request(string id, string json)
+ {
+ var response = new TaskCompletionSource();
+ _responses.AddOrUpdate(id, response, (k, e) => response);
+ await _outgoingMessageHandler.Send(json).ConfigureAwait(false);
+ return (await response.Task.ConfigureAwait(false)).Parse().ToType();
+ }
+
+ public void Dispose()
+ {
+ foreach (var t in _responses.Values)
+ {
+ t.SetCanceled();
+ }
+
+ _outputStream?.Dispose();
+ _inputStream?.Dispose();
+ CancellationTokenSource?.Dispose();
+ }
+ }
+#pragma warning restore IDE0069 // Disposable fields should be disposed
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/Models/IncomingRequest.cs b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/Models/IncomingRequest.cs
new file mode 100644
index 0000000..749a340
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/Models/IncomingRequest.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System.Text.Json;
+
+namespace AutoRest.CSharp.AutoRest.Communication.MessageHandling.Models
+{
+ internal class IncomingRequest
+ {
+ public string JsonRpc { get; } = "2.0";
+ public string? Method { get; set; }
+ public JsonElement? Params { get; set; }
+ public string? Id { get; set; }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/Models/IncomingResponse.cs b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/Models/IncomingResponse.cs
new file mode 100644
index 0000000..a6f22f0
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/Models/IncomingResponse.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+namespace AutoRest.CSharp.AutoRest.Communication.MessageHandling.Models
+{
+ internal class IncomingResponse
+ {
+ public string JsonRpc { get; } = "2.0";
+ public string? Result { get; set; }
+ public string? Id { get; set; }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/OutgoingMessageHandler.cs b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/OutgoingMessageHandler.cs
new file mode 100644
index 0000000..735ddb2
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/OutgoingMessageHandler.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using AutoRest.CSharp.AutoRest.Communication.Serialization;
+
+namespace AutoRest.CSharp.AutoRest.Communication.MessageHandling
+{
+#pragma warning disable IDE0069 // Disposable fields should be disposed
+ internal class OutgoingMessageHandler : IDisposable
+ {
+ private readonly Stream _stream;
+ private readonly CancellationToken _cancellationToken;
+ private readonly Semaphore _streamSemaphore = new Semaphore(1, 1);
+
+ public OutgoingMessageHandler(Stream stream, CancellationToken cancellationToken)
+ {
+ _stream = stream;
+ _cancellationToken = cancellationToken;
+ }
+
+ public async Task Send(string json)
+ {
+ _streamSemaphore.WaitOne();
+
+ var buffer = Encoding.UTF8.GetBytes(json);
+ var header = Encoding.ASCII.GetBytes(OutgoingMessageSerializer.Header(buffer.Length));
+ await _stream.WriteAsync(header, 0, header.Length, _cancellationToken);
+ await _stream.WriteAsync(buffer, 0, buffer.Length, _cancellationToken);
+
+ _streamSemaphore.Release();
+ }
+
+ public async Task Respond(string id, string json)
+ {
+ await Send(OutgoingMessageSerializer.Response(id, json)).ConfigureAwait(false);
+ }
+
+ public void Dispose()
+ {
+ _streamSemaphore?.Dispose();
+ }
+ }
+#pragma warning restore IDE0069 // Disposable fields should be disposed
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/PeekableBinaryStream.cs b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/PeekableBinaryStream.cs
new file mode 100644
index 0000000..1489984
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/PeekableBinaryStream.cs
@@ -0,0 +1,112 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Text;
+
+namespace AutoRest.CSharp.AutoRest.Communication.MessageHandling
+{
+ [SuppressMessage("ReSharper", "IdentifierTypo")]
+ internal class PeekableBinaryStream : IDisposable
+ {
+#pragma warning disable IDE0069 // Disposable fields should be disposed
+ private readonly Stream _stream;
+#pragma warning restore IDE0069 // Disposable fields should be disposed
+
+ private byte? _currentByte;
+ public byte? CurrentByte
+ {
+ get
+ {
+ if (_currentByte.HasValue) return _currentByte.Value;
+
+ var newByte = GetByte();
+ if (newByte.HasValue)
+ {
+ _currentByte = newByte;
+ }
+ return newByte;
+ }
+ }
+
+ private const int EndOfStream = -1;
+
+ public PeekableBinaryStream(Stream stream)
+ {
+ _stream = stream;
+ }
+
+ public void Dispose()
+ {
+ _stream.Dispose();
+ }
+
+ private byte? PopCurrentByte()
+ {
+ if (!_currentByte.HasValue) return null;
+
+ var result = _currentByte.Value;
+ _currentByte = null;
+ return result;
+ }
+
+ // Pops the current byte or reads a new one if there is no current byte
+ // Returns null if end-of-stream
+ private byte? GetByte()
+ {
+ if (_currentByte.HasValue) return PopCurrentByte();
+
+ var streamByte = _stream.ReadByte();
+ return streamByte != EndOfStream ? (byte?)streamByte : null;
+ }
+
+ public byte[] ReadBytes(int count)
+ {
+ var buffer = new byte[count];
+ var index = 0;
+ if (count > 0 && _currentByte.HasValue)
+ {
+ // ReSharper disable once PossibleInvalidOperationException
+ // ReSharper disable once PossibleNullReferenceException
+ buffer[index++] = PopCurrentByte()!.Value;
+ }
+ while (index < count)
+ {
+ index += _stream.Read(buffer, index, count - index);
+ }
+ return buffer;
+ }
+
+ public string? ReadAsciiLine()
+ {
+ var sb = new StringBuilder();
+ byte? character;
+ while ((character = GetByte()).HasValue && character != '\r' && character != '\n')
+ {
+ sb.Append((char)character.Value);
+ }
+
+ // CurrentByte will only ever be null or a non-line-ending value when this method returns since we read until line ending characters,
+ // and discard the last value if it is a '\n' preceded by a '\r'.
+ if (character == '\r' && CurrentByte == '\n')
+ {
+ GetByte();
+ }
+
+ return sb.Length != 0 ? sb.ToString() : null;
+ }
+
+ public IEnumerable ReadAllAsciiLines(Predicate? condition = null)
+ {
+ condition ??= s => s == null;
+ string? line;
+ while (condition(line = ReadAsciiLine()))
+ {
+ yield return line;
+ }
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/PeekableBinaryStreamExtensions.cs b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/PeekableBinaryStreamExtensions.cs
new file mode 100644
index 0000000..dc3ac54
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/MessageHandling/PeekableBinaryStreamExtensions.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Text;
+using System.Text.Json;
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.MessageHandling
+{
+ [SuppressMessage("ReSharper", "IdentifierTypo")]
+ internal static class PeekableBinaryStreamExtensions
+ {
+ public static bool IsJsonBlock(this byte? value) => '{' == value || '[' == value;
+
+ public static JsonElement? ReadJson(this PeekableBinaryStream stream, int contentLength) => Encoding.UTF8.GetString(stream.ReadBytes(contentLength)).Parse();
+ public static JsonElement? ReadJson(this PeekableBinaryStream stream)
+ {
+ var sb = new StringBuilder();
+ // ReSharper disable once IteratorMethodResultIsIgnored
+ stream.ReadAllAsciiLines(l => sb.Append(l).ToString().Parse() != null);
+ return sb.ToString().Parse();
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/IncomingMessageSerializer.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/IncomingMessageSerializer.cs
new file mode 100644
index 0000000..6fc1e63
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/IncomingMessageSerializer.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Threading;
+using AutoRest.CSharp.AutoRest.Communication.MessageHandling;
+using AutoRest.CSharp.AutoRest.Communication.MessageHandling.Models;
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization
+{
+ internal delegate bool ProcessAction(JsonRpcConnection connection, string pluginName, string sessionId);
+
+ internal static class IncomingMessageSerializer
+ {
+ public static string GetPluginNames(this IncomingRequest _, params string[] pluginNames) => pluginNames.ToJsonArray();
+
+ public static string Process(this IncomingRequest request, JsonRpcConnection connection, ProcessAction processAction)
+ {
+ var parameters = request.Params.ToStringArray();
+ var (pluginName, sessionId) = (parameters![0], parameters![1]);
+ return processAction(connection, pluginName, sessionId).ToJsonBool();
+ }
+
+ public static string Shutdown(this IncomingRequest _, CancellationTokenSource tokenSource)
+ {
+ tokenSource.Cancel();
+ return String.Empty;
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/ArtifactMapping.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/ArtifactMapping.cs
new file mode 100644
index 0000000..ed79f21
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/ArtifactMapping.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal class ArtifactMapping : IArtifact
+ {
+ public string? Uri { get; set; }
+ public string? Type { get; set; }
+ public string? Content { get; set; }
+ public Mapping[]? SourceMap { get; set; } = null;
+
+ public override string ToString() => $@"{{""uri"":""{Uri}"",""type"":""{Type}"",""content"":""{Content.ToStringLiteral()}""{SourceMap.TextOrEmpty($@",""sourceMap"":{SourceMap.ToJsonArray()}")}}}";
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/ArtifactMessage.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/ArtifactMessage.cs
new file mode 100644
index 0000000..d3031ad
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/ArtifactMessage.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal class ArtifactMessage : IMessage
+ {
+ public Channel Channel { get; set; }
+ public string[]? Key { get; set; } = null;
+ public IArtifact? Details { get; set; }
+ public string? Text { get; set; }
+ public SourceLocation[]? Source { get; set; } = null;
+
+ public override string ToString() =>
+ $@"{{""Channel"":""{Channel.ToString().ToLowerInvariant()}""{Key.TextOrEmpty($@",""Key"":{Key.ToJsonArray()}")},""Details"":{Details},""Text"":""{Text.ToStringLiteral()}""{Source.TextOrEmpty($@",""Source"":{Source.ToJsonArray()}")}}}";
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/ArtifactRaw.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/ArtifactRaw.cs
new file mode 100644
index 0000000..1f66d02
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/ArtifactRaw.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal class ArtifactRaw : IArtifact
+ {
+ public string? Uri { get; set; }
+ public string? Type { get; set; }
+ public string? Content { get; set; }
+ public RawSourceMap? SourceMap { get; set; } = null;
+
+ public override string ToString() => $@"{{""uri"":""{Uri}"",""type"":""{Type}"",""content"":""{Content.ToStringLiteral()}""{SourceMap.TextOrEmpty($@",""sourceMap"":{SourceMap}")}}}";
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Channel.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Channel.cs
new file mode 100644
index 0000000..9f61d97
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Channel.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ // The Channel that a message is registered with.
+ internal enum Channel
+ {
+ // Information is considered the mildest of responses; not necessarily actionable.
+ Information,
+ // Warnings are considered important for best practices, but not catastrophic in nature.
+ Warning,
+ // Errors are considered blocking issues that block a successful operation.
+ Error,
+ // Debug messages are designed for the developer to communicate internal AutoRest implementation details.
+ Debug,
+ // Verbose messages give the user additional clarity on the process.
+ Verbose,
+ // Catastrophic failure, likely aborting the process.
+ Fatal,
+ // Hint messages offer guidance or support without forcing action.
+ Hint,
+ // File represents a file output from an extension. Details are a Artifact and are required.
+ File,
+ // Content represents an update/creation of a configuration file. The final URI will be in the same folder as the primary config file.
+ Configuration,
+ // Protect is a path to not remove during a clear-output-folder.
+ Protect
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/IArtifact.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/IArtifact.cs
new file mode 100644
index 0000000..6252ce5
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/IArtifact.cs
@@ -0,0 +1,7 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal interface IArtifact { }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/IMessage.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/IMessage.cs
new file mode 100644
index 0000000..840b5b7
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/IMessage.cs
@@ -0,0 +1,7 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal interface IMessage { }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/IPosition.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/IPosition.cs
new file mode 100644
index 0000000..496d2d3
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/IPosition.cs
@@ -0,0 +1,7 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal interface IPosition { }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Mapping.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Mapping.cs
new file mode 100644
index 0000000..925a60b
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Mapping.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal class Mapping
+ {
+ public Position? Generated { get; set; }
+ public Position? Original { get; set; }
+ public string? Source { get; set; }
+ public string? Name { get; set; } = null;
+
+ public override string ToString() => $@"{{""generated"":{Generated},""original"":{Original},""source"":""{Source}""{Name.TextOrEmpty($@",""name"":""{Name}""")}}}";
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Message.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Message.cs
new file mode 100644
index 0000000..7de6eed
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Message.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal class Message : IMessage
+ {
+ public Channel Channel { get; set; }
+ public string[]? Key { get; set; } = null;
+ public object? Details { get; set; } = null;
+ public string? Text { get; set; }
+ public SourceLocation[]? Source { get; set; } = null;
+
+ public override string ToString() =>
+ $@"{{""Channel"":""{Channel.ToString().ToLowerInvariant()}""{Key.TextOrEmpty($@",""Key"":{Key.ToJsonArray()}")}{Details.TextOrEmpty($@",""Details"":{Details}")},""Text"":""{Text.ToStringLiteral()}""{Source.TextOrEmpty($@",""Source"":{Source.ToJsonArray()}")}}}";
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Position.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Position.cs
new file mode 100644
index 0000000..e2f7e09
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/Position.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal class Position : IPosition
+ {
+ // 1-based
+ public int Line { get; set; }
+ // 0-based
+ public int Column { get; set; }
+
+ public override string ToString() => $@"{{""line"":{Line},""column"":{Column}}}";
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/PositionIntPath.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/PositionIntPath.cs
new file mode 100644
index 0000000..97139a2
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/PositionIntPath.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal class PositionIntPath : IPosition
+ {
+ public int[]? Path { get; set; } = null;
+
+ public override string ToString() => $@"{{{Path.TextOrEmpty($@"""path"":{Path.ToJsonArray()}")}}}";
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/PositionStringPath.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/PositionStringPath.cs
new file mode 100644
index 0000000..6479e90
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/PositionStringPath.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal class PositionStringPath : IPosition
+ {
+ public string[]? Path { get; set; } = null;
+
+ public override string ToString() => $@"{{{Path.TextOrEmpty($@"""path"":{Path.ToJsonArray()}")}}}";
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/RawSourceMap.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/RawSourceMap.cs
new file mode 100644
index 0000000..c297604
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/RawSourceMap.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal class RawSourceMap
+ {
+ public string? File { get; set; } = null;
+ public string? SourceRoot { get; set; } = null;
+ public string? Version { get; set; }
+ public string[]? Sources { get; set; }
+ public string[]? Names { get; set; }
+ public string[]? SourcesContent { get; set; } = null;
+ public string? Mappings { get; set; }
+
+ public override string ToString() => $@"{{""version"":""{Version}"",""sources"":{Sources.ToJsonArray()},""names"":{Names.ToJsonArray()},""mappings"":""{Mappings}""{File.TextOrEmpty($@",""file"":""{File}""")}{SourceRoot.TextOrEmpty($@",""sourceRoot"":""{SourceRoot}""")}{SourcesContent.TextOrEmpty($@",""sourcesContent"":{SourcesContent.ToJsonArray()}")}}}";
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/SourceLocation.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/SourceLocation.cs
new file mode 100644
index 0000000..4087d56
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/Models/SourceLocation.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization.Models
+{
+ internal class SourceLocation
+ {
+ public string? Document { get; set; }
+ public IPosition? Position { get; set; }
+
+ public override string ToString() => $@"{{""document"":""{Document}"",""Position"":{Position}}}";
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/Serialization/OutgoingMessageSerializer.cs b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/OutgoingMessageSerializer.cs
new file mode 100644
index 0000000..8c3ffa0
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/Serialization/OutgoingMessageSerializer.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using AutoRest.CSharp.AutoRest.Communication.Serialization.Models;
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Communication.Serialization
+{
+ internal static class OutgoingMessageSerializer
+ {
+ private const string BasicRequestFormat = @"{{""jsonrpc"":""2.0"",""method"":{1},""params"":[{2},{3}],""id"":{0}}}";
+ private const string BasicNotificationFormat = @"{{""jsonrpc"":""2.0"",""method"":{0},""params"":[{1},{2}]}}";
+
+ private static string CreateRequestString(string requestId, string method, string sessionId, string? other) =>
+ String.Format(BasicRequestFormat, requestId.ToJsonStringOrNull(), method.ToJsonStringOrNull(), sessionId.ToJsonStringOrNull(), other.ToStringLiteral().ToJsonStringOrNull());
+
+ public static string ReadFile(string requestId, string sessionId, string filename) => CreateRequestString(requestId, nameof(ReadFile), sessionId, filename);
+ public static string GetValue(string requestId, string sessionId, string key) => CreateRequestString(requestId, nameof(GetValue), sessionId, key);
+ public static string ListInputs(string requestId, string sessionId, string? artifactType = null) => CreateRequestString(requestId, nameof(ListInputs), sessionId, artifactType);
+ public static string ProtectFiles(string requestId, string sessionId, string path) => CreateRequestString(requestId, nameof(ProtectFiles), sessionId, path);
+ public static string Message(string sessionId, IMessage message) => String.Format(BasicNotificationFormat, nameof(Message).ToJsonStringOrNull(), sessionId.ToJsonStringOrNull(), message);
+
+ // Custom Messages
+ public static string WriteFile(string sessionId, string filename, string content, string artifactType, RawSourceMap? sourceMap = null)
+ {
+ var artifact = new ArtifactRaw { Content = content, SourceMap = sourceMap, Type = artifactType, Uri = filename };
+ var message = new ArtifactMessage { Channel = Channel.File, Details = artifact, Text = String.Empty };
+ return Message(sessionId, message);
+ }
+ public static string WriteFile(string sessionId, string filename, string content, string artifactType, Mapping[] sourceMap)
+ {
+ var artifact = new ArtifactMapping { Content = content, SourceMap = sourceMap, Type = artifactType, Uri = filename };
+ var message = new ArtifactMessage { Channel = Channel.File, Details = artifact, Text = String.Empty };
+ return Message(sessionId, message);
+ }
+
+ private const string BasicResponseFormat = @"{{""jsonrpc"":""2.0"",""result"":{1},""id"":{0}}}";
+
+ public static string Response(string responseId, string result) => String.Format(BasicResponseFormat, responseId.ToJsonStringOrNull(), result);
+ public static string Header(int contentLength) => $"Content-Length: {contentLength}\r\n\r\n";
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Communication/StandaloneGeneratorRunner.cs b/logger/autorest.csharp/common/AutoRest/Communication/StandaloneGeneratorRunner.cs
new file mode 100644
index 0000000..14bca34
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Communication/StandaloneGeneratorRunner.cs
@@ -0,0 +1,139 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using AutoRest.CSharp.AutoRest.Plugins;
+using AutoRest.CSharp.Common.AutoRest.Plugins;
+using AutoRest.CSharp.Common.Input;
+using AutoRest.CSharp.Input;
+using Microsoft.CodeAnalysis;
+
+namespace AutoRest.CSharp.AutoRest.Communication
+{
+ internal class StandaloneGeneratorRunner
+ {
+ private static readonly string[] keepFiles = new string[] { "CodeModel.yaml", "Configuration.json", "tspCodeModel.json" };
+ public static async Task RunAsync(CommandLineOptions options)
+ {
+ string? projectPath = null;
+ string outputPath;
+ string generatedTestOutputPath;
+ bool wasProjectPathPassedIn = options.ProjectPath is not null;
+ if (options.Standalone is not null)
+ {
+ //TODO this is only here for back compat we should consider removing it
+ outputPath = options.Standalone;
+ }
+ else
+ {
+ projectPath = options.ProjectPath!;
+ if (!projectPath!.EndsWith("src", StringComparison.Ordinal))
+ projectPath = Path.Combine(projectPath, "src");
+ outputPath = Path.Combine(projectPath, "Generated");
+ }
+ generatedTestOutputPath = Path.Combine(outputPath, "..", "..", "tests", "Generated");
+ var configurationPath = options.ConfigurationPath;
+ if (configurationPath == null)
+ {
+ configurationPath = Path.Combine(outputPath, "Configuration.json");
+ if (!File.Exists(configurationPath))
+ {
+ configurationPath = Path.Combine(outputPath, "..", "..", "Configuration.json");
+ }
+ }
+ LoadConfiguration(projectPath, outputPath, options.ExistingProjectFolder, File.ReadAllText(configurationPath));
+
+ var codeModelInputPath = Path.Combine(outputPath, "CodeModel.yaml");
+ var tspInputFile = Path.Combine(outputPath, "tspCodeModel.json");
+ if (!File.Exists(tspInputFile))
+ {
+ tspInputFile = Path.Combine(outputPath, "..", "..", "tspCodeModel.json");
+ }
+
+ GeneratedCodeWorkspace workspace;
+ if (File.Exists(tspInputFile))
+ {
+ var json = await File.ReadAllTextAsync(tspInputFile);
+ var rootNamespace = TypeSpecSerialization.Deserialize(json) ?? throw new InvalidOperationException($"Deserializing {tspInputFile} has failed.");
+ workspace = await new CSharpGen().ExecuteAsync(rootNamespace);
+ if (options.IsNewProject)
+ {
+ bool needAzureKeyAuth = rootNamespace.Auth?.ApiKey != null;
+ // TODO - add support for DataFactoryElement lookup
+ await new NewProjectScaffolding(needAzureKeyAuth, false).Execute();
+ }
+ }
+ else if (File.Exists(codeModelInputPath))
+ {
+ var yaml = await File.ReadAllTextAsync(codeModelInputPath);
+ var codeModel = CodeModelSerialization.DeserializeCodeModel(yaml);
+ workspace = await new CSharpGen().ExecuteAsync(codeModel);
+ if (options.IsNewProject)
+ {
+ bool needAzureKeyAuth = codeModel.Security.Schemes.Any(scheme => scheme is KeySecurityScheme);
+ bool includeDfe = yaml.Contains("x-ms-format: dfe-", StringComparison.Ordinal);
+ await new NewProjectScaffolding(needAzureKeyAuth, includeDfe).Execute();
+ }
+ }
+ else
+ {
+ throw new InvalidOperationException($"Neither CodeModel.yaml nor tspCodeModel.json exist in {outputPath} folder.");
+ }
+
+ if (options.ClearOutputFolder)
+ {
+ DeleteDirectory(outputPath, keepFiles);
+ DeleteDirectory(generatedTestOutputPath, keepFiles);
+ }
+
+ await foreach (var file in workspace.GetGeneratedFilesAsync())
+ {
+ if (string.IsNullOrEmpty(file.Text))
+ {
+ continue;
+ }
+ var filename = Path.Combine(outputPath, file.Name);
+ Console.WriteLine($"Writing {filename}");
+ Directory.CreateDirectory(Path.GetDirectoryName(filename)!);
+ await File.WriteAllTextAsync(filename, file.Text);
+ }
+ }
+
+ private static void DeleteDirectory(string path, string[] keepFiles)
+ {
+ var directoryInfo = new DirectoryInfo(path);
+ if (!directoryInfo.Exists)
+ {
+ return;
+ }
+ foreach (FileInfo file in directoryInfo.GetFiles())
+ {
+ if (keepFiles.Contains(file.Name))
+ {
+ continue;
+ }
+ file.Delete();
+ }
+
+ foreach (DirectoryInfo directory in directoryInfo.GetDirectories())
+ {
+ DeleteDirectory(directory.FullName, keepFiles);
+ }
+
+ if (!directoryInfo.EnumerateFileSystemInfos().Any())
+ {
+ directoryInfo.Delete();
+ }
+ }
+
+ internal static void LoadConfiguration(string? projectPath, string outputPath, string? existingProjectFolder, string json)
+ {
+ var root = JsonDocument.Parse(json).RootElement;
+ Configuration.LoadConfiguration(root, projectPath, outputPath, existingProjectFolder);
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Plugins/CSharpGen.cs b/logger/autorest.csharp/common/AutoRest/Plugins/CSharpGen.cs
new file mode 100644
index 0000000..0661338
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Plugins/CSharpGen.cs
@@ -0,0 +1,183 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using AutoRest.CSharp.AutoRest.Communication;
+using AutoRest.CSharp.Common.AutoRest.Plugins;
+using AutoRest.CSharp.Common.Input;
+using AutoRest.CSharp.Common.Utilities;
+using AutoRest.CSharp.Input;
+using AutoRest.CSharp.Input.Source;
+using AutoRest.CSharp.Mgmt.AutoRest;
+using AutoRest.CSharp.Mgmt.Decorator;
+using AutoRest.CSharp.Mgmt.Report;
+using AutoRest.CSharp.Output.Models.Types;
+using AutoRest.CSharp.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace AutoRest.CSharp.AutoRest.Plugins
+{
+ [PluginName("csharpgen")]
+ internal class CSharpGen : IPlugin
+ {
+ public async Task ExecuteAsync(CodeModel codeModel)
+ {
+ ValidateConfiguration();
+
+ Directory.CreateDirectory(Configuration.OutputFolder);
+ var project = await GeneratedCodeWorkspace.Create(Configuration.AbsoluteProjectFolder, Configuration.OutputFolder, Configuration.SharedSourceFolders);
+ var sourceInputModel = new SourceInputModel(await project.GetCompilationAsync());
+
+ var schemaUsageProvider = new SchemaUsageProvider(codeModel); // Create schema usage before transformation applied
+ if (Configuration.Generation1ConvenienceClient)
+ {
+ CodeModelTransformer.TransformForDataPlane(codeModel);
+ var inputNamespace = new CodeModelConverter(codeModel, schemaUsageProvider).CreateNamespace();
+ DataPlaneTarget.Execute(project, inputNamespace, sourceInputModel);
+ }
+ else if (Configuration.AzureArm)
+ {
+ CodeModelTransformer.TransformForMgmt(codeModel);
+ var inputNamespace = new CodeModelConverter(codeModel, schemaUsageProvider).CreateNamespace();
+ MgmtContext.Initialize(new BuildContext(inputNamespace, sourceInputModel));
+ await MgmtTarget.ExecuteAsync(project);
+ if (Configuration.MgmtTestConfiguration is not null && !Configuration.MgmtConfiguration.MgmtDebug.ReportOnly)
+ await MgmtTestTarget.ExecuteAsync(project, inputNamespace, sourceInputModel);
+ GenerateMgmtReport(project);
+ }
+ else
+ {
+ var inputNamespace = new CodeModelConverter(codeModel, schemaUsageProvider).CreateNamespace();
+ await LowLevelTarget.ExecuteAsync(project, inputNamespace, sourceInputModel, false);
+ }
+ return project;
+ }
+
+ private void GenerateMgmtReport(GeneratedCodeWorkspace project)
+ {
+ MgmtReport.Instance.TransformSection.ForEachTransform((t, usages) =>
+ {
+ string[] ignoreNoUsage = new string[]
+ {
+ TransformTypeName.AcronymMapping,
+ TransformTypeName.FormatByNameRules
+ };
+ if (usages.Count == 0 && !ignoreNoUsage.Contains(t.TransformType))
+ AutoRestLogger.Warning($"No usage transform detected: {t}").Wait();
+ });
+ if (Configuration.MgmtConfiguration.MgmtDebug.GenerateReport)
+ {
+ string report = MgmtReport.Instance.GenerateReport(Configuration.MgmtConfiguration.MgmtDebug.ReportFormat);
+ project.AddPlainFiles("_mgmt-codegen-report.log", report);
+ }
+ }
+
+ public async Task ExecuteAsync(InputNamespace rootNamespace)
+ {
+ ValidateConfiguration();
+
+ Directory.CreateDirectory(Configuration.OutputFolder);
+ var project = await GeneratedCodeWorkspace.Create(Configuration.AbsoluteProjectFolder, Configuration.OutputFolder, Configuration.SharedSourceFolders);
+ var sourceInputModel = new SourceInputModel(await project.GetCompilationAsync(), await ProtocolCompilationInput.TryCreate());
+
+ if (Configuration.AzureArm)
+ {
+ InputNamespaceTransformer.Transform(rootNamespace);
+ MgmtContext.Initialize(new BuildContext(rootNamespace, sourceInputModel));
+ await MgmtTarget.ExecuteAsync(project);
+ if (Configuration.GenerateSampleProject)
+ await MgmtTestTarget.ExecuteAsync(project, rootNamespace, sourceInputModel);
+ }
+ else
+ {
+ await LowLevelTarget.ExecuteAsync(project, rootNamespace, sourceInputModel, true);
+ }
+ return project;
+ }
+
+ private static void ValidateConfiguration()
+ {
+ if (Configuration.Generation1ConvenienceClient && Configuration.AzureArm)
+ {
+ throw new Exception("Enabling both 'generation1-convenience-client' and 'azure-arm' at the same time is not supported.");
+ }
+ }
+
+ public async Task Execute(IPluginCommunication autoRest)
+ {
+ Console.SetOut(Console.Error); //if you send anything to stdout there is an autorest error so this protects us against that happening
+ string? codeModelFileName = (await autoRest.ListInputs()).FirstOrDefault();
+ if (string.IsNullOrEmpty(codeModelFileName))
+ throw new Exception("Generator did not receive the code model file.");
+
+ string codeModelYaml = await autoRest.ReadFile(codeModelFileName);
+ CodeModel codeModel = CodeModelSerialization.DeserializeCodeModel(codeModelYaml);
+
+ Configuration.Initialize(autoRest, codeModel.Language.Default.Name, codeModel.Language.Default.Name);
+
+ if (!Path.IsPathRooted(Configuration.OutputFolder))
+ {
+ await AutoRestLogger.Warning("output-folder path should be an absolute path");
+ }
+ if (Configuration.SaveInputs)
+ {
+ await autoRest.WriteFile("Configuration.json", Configuration.SaveConfiguration(), "source-file-csharp");
+ await autoRest.WriteFile("CodeModel.yaml", codeModelYaml, "source-file-csharp");
+ }
+
+ try
+ {
+ // generate source code
+ var project = await ExecuteAsync(codeModel);
+ await foreach (var file in project.GetGeneratedFilesAsync())
+ {
+ // format all \ to / in filename, otherwise they will be treated as escape char when sending to autorest service
+ var filename = file.Name.Replace('\\', '/');
+ await autoRest.WriteFile(filename, file.Text, "source-file-csharp");
+ }
+
+ // generate csproj if necessary
+ if (!Configuration.SkipCSProj)
+ {
+ bool needAzureKeyAuth = codeModel.Security.Schemes.Any(scheme => scheme is KeySecurityScheme);
+ bool includeDfe = codeModelYaml.Contains("x-ms-format: dfe-", StringComparison.Ordinal);
+ if (Configuration.OutputFolder.EndsWith("/src/Generated"))
+ {
+ await new NewProjectScaffolding(needAzureKeyAuth, includeDfe).Execute();
+ }
+ else
+ {
+ new CSharpProj(needAzureKeyAuth, includeDfe).Execute(autoRest);
+ }
+ }
+ }
+ catch (ErrorHelpers.ErrorException e)
+ {
+ await AutoRestLogger.Fatal(e.ErrorText);
+ return false;
+ }
+ catch (Exception)
+ {
+ try
+ {
+ if (Configuration.SaveInputs)
+ {
+ // We are unsuspectingly crashing, so output anything that might help us reproduce the issue
+ File.WriteAllText(Path.Combine(Configuration.OutputFolder, "Configuration.json"), Configuration.SaveConfiguration());
+ File.WriteAllText(Path.Combine(Configuration.OutputFolder, "CodeModel.yaml"), codeModelYaml);
+ }
+ }
+ catch
+ {
+ // Ignore any errors while trying to output crash information
+ }
+ throw;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Plugins/CSharpProj.cs b/logger/autorest.csharp/common/AutoRest/Plugins/CSharpProj.cs
new file mode 100644
index 0000000..77c13de
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Plugins/CSharpProj.cs
@@ -0,0 +1,183 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Reflection;
+using AutoRest.CSharp.AutoRest.Communication;
+using AutoRest.CSharp.Common.Input;
+using AutoRest.CSharp.Generation.Writers;
+
+namespace AutoRest.CSharp.AutoRest.Plugins
+{
+ // TODO -- move this somewhere else because it is no longer a "plugin"
+ internal class CSharpProj
+ {
+ private readonly bool _needAzureKeyAuth;
+ private readonly bool _includeDfe;
+
+ public CSharpProj(bool needAzureKeyAuth, bool includeDfe)
+ {
+ _needAzureKeyAuth = needAzureKeyAuth;
+ _includeDfe = includeDfe;
+ }
+
+ private static string GetVersion()
+ {
+ Assembly clientAssembly = Assembly.GetExecutingAssembly();
+
+ AssemblyInformationalVersionAttribute? versionAttribute = clientAssembly.GetCustomAttribute();
+ if (versionAttribute == null)
+ {
+ throw new InvalidOperationException($"{nameof(AssemblyInformationalVersionAttribute)} is required on client SDK assembly '{clientAssembly.FullName}'");
+ }
+
+ string version = versionAttribute.InformationalVersion;
+
+ int hashSeparator = version.IndexOf('+');
+ if (hashSeparator != -1)
+ {
+ return version.Substring(0, hashSeparator);
+ }
+
+ return version;
+ }
+
+ public void Execute(IPluginCommunication autoRest)
+ => WriteCSProjFiles(async (filename, text) =>
+ {
+ await autoRest.WriteFile(Path.Combine(Configuration.RelativeProjectFolder, filename), text, "source-file-csharp");
+ });
+
+ public void Execute()
+ => WriteCSProjFiles(async (filename, text) =>
+ {
+ //TODO adding to workspace makes the formatting messed up since its a raw xml document
+ //somewhere it tries to parse it as a syntax tree and when it converts back to text
+ //its no longer valid xml. We should consider a "raw files" concept in the work space
+ //so the file writing can still remain in one place
+ await File.WriteAllTextAsync(Path.Combine(Configuration.AbsoluteProjectFolder, filename), text);
+ });
+
+ private void WriteCSProjFiles(Action writeFile)
+ {
+ // write src csproj
+ var csprojContent = Configuration.SkipCSProjPackageReference ? GetCSProj() : GetExternalCSProj();
+ writeFile($"{Configuration.Namespace}.csproj", csprojContent);
+
+ // write test csproj when needed
+ if (Configuration.MgmtTestConfiguration is not null)
+ {
+ var testCSProjContent = GetTestCSProj();
+ string testGenProjectFolder;
+ if (Configuration.MgmtTestConfiguration.OutputFolder is { } testGenProjectOutputFolder)
+ {
+ testGenProjectFolder = Path.Combine(testGenProjectOutputFolder, "../");
+ }
+ else
+ {
+ testGenProjectFolder = "../";
+ }
+ Console.WriteLine(Path.Combine(testGenProjectFolder, $"{Configuration.Namespace}.Tests.csproj"));
+ writeFile(FormatPath(Path.Combine(testGenProjectFolder, $"{Configuration.Namespace}.Tests.csproj")), testCSProjContent);
+ }
+ }
+
+ private static string FormatPath(string? path)
+ {
+ if (string.IsNullOrEmpty(path))
+ return path ?? "";
+ return Path.GetFullPath(path.TrimEnd('/', '\\')).Replace("\\", "/");
+ }
+
+ private string GetTestCSProj()
+ {
+ var writer = new CSProjWriter()
+ {
+ TargetFramework = "netstandard2.0",
+ TreatWarningsAsErrors = true,
+ Nullable = "annotations",
+ IncludeManagementSharedCode = Configuration.AzureArm ? true : null,
+ };
+
+ writer.ProjectReferences.Add(new($"..\\src\\{Configuration.Namespace}.csproj"));
+
+ writer.PackageReferences.Add(new("NUnit"));
+ writer.PackageReferences.Add(new("Azure.Identity"));
+
+ writer.CompileIncludes.Add(new("..\\..\\..\\..\\src\\assets\\TestFramework\\MockTestBase.cs"));
+ writer.CompileIncludes.Add(new("..\\..\\..\\..\\src\\assets\\TestFramework\\RecordedTestAttribute.cs"));
+
+ return writer.Write();
+ }
+
+ private string GetCSProj()
+ {
+ var builder = new CSProjWriter()
+ {
+ TargetFramework = "netstandard2.0",
+ TreatWarningsAsErrors = true,
+ Nullable = "annotations",
+ IncludeManagementSharedCode = Configuration.AzureArm ? true : null,
+ DefineConstants = !Configuration.AzureArm && !Configuration.Generation1ConvenienceClient ? new("$(DefineConstants);EXPERIMENTAL") : null
+ };
+ builder.PackageReferences.Add(new("Azure.Core"));
+ if (_includeDfe)
+ {
+ builder.PackageReferences.Add(new("Azure.Core.Expressions.DataFactory"));
+ }
+
+ if (Configuration.AzureArm)
+ {
+ builder.PackageReferences.Add(new("Azure.ResourceManager"));
+ }
+ else if (!Configuration.Generation1ConvenienceClient)
+ {
+ builder.PackageReferences.Add(new("Azure.Core.Experimental"));
+ }
+
+ if (Configuration.UseModelReaderWriter)
+ {
+ builder.PackageReferences.Add(new("System.ClientModel"));
+ }
+
+ if (_needAzureKeyAuth)
+ {
+ builder.CompileIncludes.Add(new("$(AzureCoreSharedSources)AzureKeyCredentialPolicy.cs", "Shared/Core"));
+ }
+
+ return builder.Write();
+ }
+
+ private string GetExternalCSProj()
+ {
+ var writer = new CSProjWriter()
+ {
+ TargetFramework = "netstandard2.0",
+ TreatWarningsAsErrors = true,
+ Nullable = "annotations",
+ IncludeManagementSharedCode = Configuration.AzureArm ? true : null,
+ DefineConstants = !Configuration.AzureArm && !Configuration.Generation1ConvenienceClient ? new("$(DefineConstants);EXPERIMENTAL") : null,
+ LangVersion = "11.0",
+ IncludeGeneratorSharedCode = true,
+ RestoreAdditionalProjectSources = "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-net/nuget/v3/index.json"
+ };
+ writer.PackageReferences.Add(new("Azure.Core"));
+ if (_includeDfe)
+ {
+ writer.PackageReferences.Add(new("Azure.Core.Expressions.DataFactory"));
+ }
+
+ if (Configuration.UseModelReaderWriter)
+ {
+ writer.PackageReferences.Add(new("System.ClientModel"));
+ }
+
+ var version = GetVersion();
+
+ writer.PrivatePackageReferences.Add(new("Microsoft.Azure.AutoRest.CSharp", version));
+
+ return writer.Write();
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Plugins/GeneratedCodeWorkspace.cs b/logger/autorest.csharp/common/AutoRest/Plugins/GeneratedCodeWorkspace.cs
new file mode 100644
index 0000000..322badc
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Plugins/GeneratedCodeWorkspace.cs
@@ -0,0 +1,326 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.ClientModel;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using AutoRest.CSharp.Common.AutoRest.Plugins;
+using AutoRest.CSharp.Common.Input;
+using AutoRest.CSharp.Common.Output.PostProcessing;
+using Azure;
+using Azure.Core;
+using Azure.ResourceManager;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Simplification;
+
+namespace AutoRest.CSharp.AutoRest.Plugins
+{
+ internal class GeneratedCodeWorkspace
+ {
+ public static readonly string SharedFolder = "shared";
+ public static readonly string GeneratedFolder = "Generated";
+ public static readonly string GeneratedTestFolder = "GeneratedTests";
+
+ private static readonly IReadOnlyList AssemblyMetadataReferences;
+
+ private static readonly CSharpSyntaxRewriter SA1505Rewriter = new SA1505Rewriter();
+
+ static GeneratedCodeWorkspace()
+ {
+ var references = new List
+ {
+ MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(Response).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ClientResult).Assembly.Location),
+ MetadataReference.CreateFromFile(typeof(ArmResource).Assembly.Location),
+ };
+
+ var trustedAssemblies = ((string?)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") ?? "").Split(Path.PathSeparator);
+ foreach (var tpl in trustedAssemblies)
+ {
+ references.Add(MetadataReference.CreateFromFile(tpl));
+ }
+
+ AssemblyMetadataReferences = references;
+ }
+
+ private static readonly string[] SharedFolders = { SharedFolder };
+ private static readonly string[] GeneratedFolders = { GeneratedFolder };
+ private static readonly string[] GeneratedTestFolders = { GeneratedFolder, GeneratedTestFolder };
+ private static Task? _cachedProject;
+
+ private Project _project;
+ private Dictionary _xmlDocFiles { get; }
+ private Dictionary _plainFiles { get; }
+
+ private GeneratedCodeWorkspace(Project generatedCodeProject)
+ {
+ _project = generatedCodeProject;
+ _xmlDocFiles = new();
+ _plainFiles = new();
+ }
+
+ ///
+ /// Creating AdHoc workspace and project takes a while, we'd like to preload this work
+ /// to the generator startup time
+ ///
+ public static void Initialize()
+ {
+ _cachedProject = Task.Run(CreateGeneratedCodeProject);
+ }
+
+ public void AddGeneratedFile(string name, string text) => AddGeneratedFile(name, text, GeneratedFolders);
+
+ public void AddGeneratedTestFile(string name, string text) => AddGeneratedFile(name, text, GeneratedTestFolders);
+
+ private void AddGeneratedFile(string name, string text, string[] folders)
+ {
+ var document = _project.AddDocument(name, text, folders);
+ var root = document.GetSyntaxRootAsync().GetAwaiter().GetResult();
+ Debug.Assert(root != null);
+
+ root = root.WithAdditionalAnnotations(Simplifier.Annotation);
+ document = document.WithSyntaxRoot(root);
+ _project = document.Project;
+ }
+
+ ///
+ /// Add generated doc file.
+ ///
+ /// Name of the doc file, including the relative path to the "Generated" folder.
+ /// Content of the doc file.
+ public void AddGeneratedDocFile(string name, XmlDocumentFile xmlDocument)
+ {
+ _xmlDocFiles.Add(name, xmlDocument);
+ }
+
+ public void AddPlainFiles(string name, string content)
+ {
+ _plainFiles.Add(name, content);
+ }
+
+ public async IAsyncEnumerable<(string Name, string Text)> GetGeneratedFilesAsync()
+ {
+ var compilation = await _project.GetCompilationAsync();
+ Debug.Assert(compilation != null);
+
+ var suppressedTypeNames = GetSuppressedTypeNames(compilation);
+ List> documents = new List>();
+ foreach (Document document in _project.Documents)
+ {
+ // Skip writing shared files or originals
+ if (!IsGeneratedDocument(document))
+ {
+ continue;
+ }
+
+ documents.Add(ProcessDocument(compilation, document, suppressedTypeNames));
+ }
+ var docs = await Task.WhenAll(documents);
+ var needProcessGeneratedDocs = _xmlDocFiles.Any();
+ var generatedDocs = new Dictionary();
+
+ foreach (var doc in docs)
+ {
+ var processed = doc;
+
+ var text = await processed.GetSyntaxTreeAsync();
+ yield return (processed.Name, text!.ToString());
+ if (needProcessGeneratedDocs) // TODO -- this is a workaround. In HLC, in some cases, there are multiple documents with the same name added in this list, and we get "dictionary same key has been added" exception
+ generatedDocs.Add(processed.Name, text);
+ }
+
+ foreach (var (docName, doc) in _xmlDocFiles)
+ {
+ var xmlWriter = doc.XmlDocWriter;
+ if (generatedDocs.TryGetValue(doc.TestFileName, out var testDocument))
+ {
+ var content = await XmlFormatter.FormatAsync(xmlWriter, testDocument);
+ yield return (docName, content);
+ }
+ }
+
+ foreach (var (file, content) in _plainFiles)
+ {
+ yield return (file, content);
+ }
+ }
+
+ private async Task ProcessDocument(Compilation compilation, Document document, ImmutableHashSet suppressedTypeNames)
+ {
+ var syntaxTree = await document.GetSyntaxTreeAsync();
+ if (syntaxTree != null)
+ {
+ var semanticModel = compilation.GetSemanticModel(syntaxTree);
+ var modelRemoveRewriter = new MemberRemoverRewriter(_project, semanticModel, suppressedTypeNames);
+ document = document.WithSyntaxRoot(SA1505Rewriter.Visit(modelRemoveRewriter.Visit(await syntaxTree.GetRootAsync())));
+ }
+
+ document = await Simplifier.ReduceAsync(document);
+ document = await Formatter.FormatAsync(document);
+ return document;
+ }
+
+ internal static ImmutableHashSet GetSuppressedTypeNames(Compilation compilation)
+ {
+ var suppressTypeAttribute = compilation.GetTypeByMetadataName(typeof(CodeGenSuppressTypeAttribute).FullName!)!;
+ return compilation.Assembly.GetAttributes()
+ .Where(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, suppressTypeAttribute))
+ .Select(a => a.ConstructorArguments[0].Value)
+ .OfType()
+ .ToImmutableHashSet();
+ }
+
+ ///
+ /// Add some additional files into this project
+ ///
+ ///
+ ///
+ ///
+ public void AddDirectory(string directory, Func? skipPredicate = null, IEnumerable? folders = null)
+ {
+ _project = AddDirectory(_project, directory, skipPredicate, folders);
+ }
+
+ ///
+ /// Add the files in the directory to a project per a given predicate with the folders specified
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static Project AddDirectory(Project project, string directory, Func? skipPredicate = null, IEnumerable? folders = null)
+ {
+ foreach (string sourceFile in Directory.GetFiles(directory, "*.cs", SearchOption.AllDirectories))
+ {
+ if (skipPredicate != null && skipPredicate(sourceFile))
+ continue;
+
+ project = project.AddDocument(sourceFile, File.ReadAllText(sourceFile), folders ?? Array.Empty(), sourceFile).Project;
+ }
+
+ return project;
+ }
+
+ public static async Task Create(string projectDirectory, string outputDirectory, string[] sharedSourceFolders)
+ {
+ var projectTask = Interlocked.Exchange(ref _cachedProject, null);
+ var generatedCodeProject = projectTask != null ? await projectTask : CreateGeneratedCodeProject();
+
+ if (Path.IsPathRooted(projectDirectory) && Path.IsPathRooted(outputDirectory))
+ {
+ projectDirectory = Path.GetFullPath(projectDirectory);
+ outputDirectory = Path.GetFullPath(outputDirectory);
+
+ generatedCodeProject = AddDirectory(generatedCodeProject, projectDirectory, skipPredicate: sourceFile => sourceFile.StartsWith(outputDirectory));
+ }
+
+ foreach (var sharedSourceFolder in sharedSourceFolders)
+ {
+ generatedCodeProject = AddDirectory(generatedCodeProject, sharedSourceFolder, folders: SharedFolders);
+ }
+
+ generatedCodeProject = generatedCodeProject.WithParseOptions(new CSharpParseOptions(preprocessorSymbols: new[] { "EXPERIMENTAL" }));
+ return new GeneratedCodeWorkspace(generatedCodeProject);
+ }
+
+ // TODO: Currently the outputDirectory is expected to be generated folder. We will handle the customization folder if there is a case.
+ public static GeneratedCodeWorkspace CreateExistingCodeProject(string outputDirectory)
+ {
+ var workspace = new AdhocWorkspace();
+ Project project = workspace.AddProject("ExistingCode", LanguageNames.CSharp);
+
+ if (Path.IsPathRooted(outputDirectory))
+ {
+ outputDirectory = Path.GetFullPath(outputDirectory);
+ project = AddDirectory(project, outputDirectory, null);
+ }
+
+ project = project
+ .AddMetadataReferences(AssemblyMetadataReferences)
+ .WithCompilationOptions(new CSharpCompilationOptions(
+ OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Disable));
+
+ return new GeneratedCodeWorkspace(project);
+ }
+
+ public static async Task CreatePreviousContractFromDll(string xmlDocumentationpath, string dllPath)
+ {
+ var workspace = new AdhocWorkspace();
+ Project project = workspace.AddProject("PreviousContract", LanguageNames.CSharp);
+ project = project
+ .AddMetadataReferences(AssemblyMetadataReferences)
+ .WithCompilationOptions(new CSharpCompilationOptions(
+ OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Disable));
+ project = project.AddMetadataReference(MetadataReference.CreateFromFile(dllPath, documentation: XmlDocumentationProvider.CreateFromFile(xmlDocumentationpath)));
+ return await project.GetCompilationAsync();
+ }
+
+ private static Project CreateGeneratedCodeProject()
+ {
+ var workspace = new AdhocWorkspace();
+ // TODO: This is not the right way to construct the workspace but it works
+ Project generatedCodeProject = workspace.AddProject("GeneratedCode", LanguageNames.CSharp);
+
+ generatedCodeProject = generatedCodeProject
+ .AddMetadataReferences(AssemblyMetadataReferences)
+ .WithCompilationOptions(new CSharpCompilationOptions(
+ OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Disable));
+ return generatedCodeProject;
+ }
+
+ public async Task GetCompilationAsync()
+ {
+ var compilation = await _project.GetCompilationAsync() as CSharpCompilation;
+ Debug.Assert(compilation != null);
+ return compilation;
+ }
+
+ public static bool IsCustomDocument(Document document) => !IsGeneratedDocument(document) && !IsSharedDocument(document);
+ public static bool IsSharedDocument(Document document) => document.Folders.Contains(SharedFolder);
+ public static bool IsGeneratedDocument(Document document) => document.Folders.Contains(GeneratedFolder);
+ public static bool IsGeneratedTestDocument(Document document) => document.Folders.Contains(GeneratedTestFolder);
+
+ ///
+ /// This method delegates the caller to do something on the generated code project
+ ///
+ ///
+ ///
+ public async Task PostProcess(Func> processor)
+ {
+ _project = await processor(_project);
+ }
+
+ ///
+ /// This method invokes the postProcessor to do some post processing work
+ /// Depending on the configuration, it will either remove + internalize, just internalize or do nothing
+ ///
+ ///
+ ///
+ public async Task PostProcessAsync(PostProcessor? postProcessor = null)
+ {
+ postProcessor ??= new PostProcessor(ImmutableHashSet.Empty);
+ switch (Configuration.UnreferencedTypesHandling)
+ {
+ case Configuration.UnreferencedTypesHandlingOption.KeepAll:
+ break;
+ case Configuration.UnreferencedTypesHandlingOption.Internalize:
+ _project = await postProcessor.InternalizeAsync(_project);
+ break;
+ case Configuration.UnreferencedTypesHandlingOption.RemoveOrInternalize:
+ _project = await postProcessor.InternalizeAsync(_project);
+ _project = await postProcessor.RemoveAsync(_project);
+ break;
+ }
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Plugins/IPlugin.cs b/logger/autorest.csharp/common/AutoRest/Plugins/IPlugin.cs
new file mode 100644
index 0000000..15dd7d0
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Plugins/IPlugin.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Threading.Tasks;
+using AutoRest.CSharp.AutoRest.Communication;
+using AutoRest.CSharp.Input;
+
+namespace AutoRest.CSharp.AutoRest.Plugins
+{
+ internal interface IPlugin
+ {
+ Task Execute(IPluginCommunication autoRest);
+ }
+
+ [AttributeUsage(AttributeTargets.Class)]
+ internal class PluginNameAttribute : Attribute
+ {
+ public string PluginName { get; }
+
+ public PluginNameAttribute(string pluginName)
+ {
+ PluginName = pluginName;
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Plugins/MemberRemoverRewriter.cs b/logger/autorest.csharp/common/AutoRest/Plugins/MemberRemoverRewriter.cs
new file mode 100644
index 0000000..c9601ee
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Plugins/MemberRemoverRewriter.cs
@@ -0,0 +1,303 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using AutoRest.CSharp.Input.Source;
+using AutoRest.CSharp.Utilities;
+using Azure.Core;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace AutoRest.CSharp.AutoRest.Plugins
+{
+#pragma warning disable RS1024
+ internal class MemberRemoverRewriter : CSharpSyntaxRewriter
+ {
+ private static readonly SymbolDisplayFormat _fullyQualifiedNameFormat
+ = new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);
+
+ private readonly Project _project;
+ private readonly SemanticModel _semanticModel;
+ private readonly ImmutableHashSet _suppressedTypeNames;
+ private readonly Dictionary> _suppressionCache;
+
+ public MemberRemoverRewriter(Project project, SemanticModel semanticModel, ImmutableHashSet suppressedTypeNames)
+ {
+ _project = project;
+ _semanticModel = semanticModel;
+ _suppressedTypeNames = suppressedTypeNames;
+ _suppressionCache = new Dictionary>();
+ }
+
+ public override SyntaxNode? VisitCompilationUnit(CompilationUnitSyntax node)
+ {
+ var visitedNode = base.VisitCompilationUnit(node);
+ return visitedNode is CompilationUnitSyntax cu && !cu.Members.Any() ? SyntaxFactory.CompilationUnit() : visitedNode;
+ }
+
+ public override SyntaxNode? VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
+ {
+ var visitedNode = base.VisitNamespaceDeclaration(node);
+ return visitedNode is NamespaceDeclarationSyntax ns && !ns.Members.Any() ? null : visitedNode;
+ }
+
+ public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node)
+ => IsSuppressedType(node) ? null : base.VisitClassDeclaration(node);
+
+ public override SyntaxNode? VisitStructDeclaration(StructDeclarationSyntax node)
+ => IsSuppressedType(node) ? null : base.VisitStructDeclaration(node);
+
+ public override SyntaxNode? VisitEnumDeclaration(EnumDeclarationSyntax node)
+ => IsSuppressedType(node) ? null : base.VisitEnumDeclaration(node);
+
+ public override SyntaxNode? VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
+ {
+ var symbol = _semanticModel.GetDeclaredSymbol(node);
+ return ShouldRemoveMember(symbol) ? null : base.VisitConstructorDeclaration(node);
+ }
+
+ public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node)
+ {
+ var symbol = _semanticModel.GetDeclaredSymbol(node);
+ return ShouldRemoveMember(symbol) ? null : base.VisitPropertyDeclaration(node);
+ }
+
+ public override SyntaxNode? VisitMethodDeclaration(MethodDeclarationSyntax node)
+ {
+ var symbol = _semanticModel.GetDeclaredSymbol(node);
+ return ShouldRemoveMember(symbol) ? null : base.VisitMethodDeclaration(node);
+ }
+
+ public override SyntaxNode? VisitOperatorDeclaration(OperatorDeclarationSyntax node)
+ {
+ var symbol = _semanticModel.GetDeclaredSymbol(node);
+ return ShouldRemoveMember(symbol) ? null : base.VisitOperatorDeclaration(node);
+ }
+
+ public override SyntaxNode? VisitConversionOperatorDeclaration(ConversionOperatorDeclarationSyntax node)
+ {
+ var symbol = _semanticModel.GetDeclaredSymbol(node);
+ return ShouldRemoveMember(symbol) ? null : base.VisitConversionOperatorDeclaration(node);
+ }
+
+ public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node)
+ {
+ var symbol = node.Declaration.Variables.Count == 1
+ ? _semanticModel.GetDeclaredSymbol(node.Declaration.Variables[0])
+ : null;
+
+ return ShouldRemoveMember(symbol) ? null : base.VisitFieldDeclaration(node);
+ }
+
+ private List? GetSupressions(INamedTypeSymbol namedTypeSymbol)
+ {
+ if (_suppressionCache.TryGetValue(namedTypeSymbol, out var suppressions))
+ {
+ return suppressions;
+ }
+
+ foreach (var attributeData in namedTypeSymbol.GetAttributes())
+ {
+ if (attributeData.AttributeClass?.Name.Equals(CodeGenAttributes.CodeGenSuppressAttributeName) == true)
+ {
+ ValidateArguments(namedTypeSymbol, attributeData);
+
+ suppressions ??= new List();
+ var name = attributeData.ConstructorArguments[0].Value as string;
+ var parameterTypes = attributeData.ConstructorArguments[1].Values.Select(v => (ISymbol?)v.Value).ToArray();
+ suppressions.Add(new Supression(name, parameterTypes));
+ }
+ }
+
+ if (suppressions != null)
+ {
+ _suppressionCache.Add(namedTypeSymbol, suppressions);
+ }
+ return suppressions;
+
+ static void ValidateArguments(INamedTypeSymbol typeSymbol, AttributeData attributeData)
+ {
+ var arguments = attributeData.ConstructorArguments;
+ if (arguments.Length == 0)
+ {
+ var fullName = typeSymbol.ToDisplayString(_fullyQualifiedNameFormat);
+ ErrorHelpers.ThrowError($"CodeGenSuppress attribute on {fullName} must specify a method name as its first argument.");
+ }
+
+ if (arguments.Length == 1 || arguments[0].Kind != TypedConstantKind.Primitive || arguments[0].Value is not string)
+ {
+ var attribute = attributeData.ApplicationSyntaxReference.GetText();
+ var fullName = typeSymbol.ToDisplayString(_fullyQualifiedNameFormat);
+ ErrorHelpers.ThrowError($"{attribute} attribute on {fullName} must specify a method name as its first argument.");
+ }
+
+ if (arguments.Length == 2 && arguments[1].Kind == TypedConstantKind.Array)
+ {
+ ValidateTypeArguments(typeSymbol, attributeData, arguments[1].Values);
+ }
+ else
+ {
+ ValidateTypeArguments(typeSymbol, attributeData, arguments.Skip(1));
+ }
+ }
+
+ static void ValidateTypeArguments(INamedTypeSymbol typeSymbol, AttributeData attributeData, IEnumerable arguments)
+ {
+ foreach (var argument in arguments)
+ {
+ if (argument.Kind == TypedConstantKind.Type)
+ {
+ if (argument.Value is IErrorTypeSymbol errorType)
+ {
+ var attribute = attributeData.ApplicationSyntaxReference.GetText();
+ var fileLinePosition = attributeData.ApplicationSyntaxReference.GetFileLinePosition();
+ var filePath = fileLinePosition.Path;
+ var line = fileLinePosition.StartLinePosition.Line + 1;
+ ErrorHelpers.ThrowError($"The undefined type '{errorType.Name}' is referenced in the '{attribute}' attribute ({filePath}, line: {line}). Please define this type or remove it from the attribute.");
+ }
+ }
+ else
+ {
+ var fullName = typeSymbol.ToDisplayString(_fullyQualifiedNameFormat);
+ var attribute = attributeData.ApplicationSyntaxReference.GetText();
+ ErrorHelpers.ThrowError($"Argument '{argument.ToCSharpString()}' in attribute '{attribute}' applied to '{fullName}' must be a type.");
+ }
+ }
+ }
+ }
+
+ private bool IsSuppressedType(BaseTypeDeclarationSyntax typeSyntax)
+ {
+ if (_suppressedTypeNames.IsEmpty)
+ {
+ return false;
+ }
+
+ var typeSymbol = _semanticModel.GetDeclaredSymbol(typeSyntax);
+ while (typeSymbol != null)
+ {
+ var fullName = typeSymbol.ToDisplayString(_fullyQualifiedNameFormat);
+ if (_suppressedTypeNames.Contains(typeSymbol.Name) || _suppressedTypeNames.Contains(fullName))
+ {
+ return true;
+ }
+
+ typeSymbol = SymbolEqualityComparer.Default.Equals(typeSymbol.BaseType?.ContainingAssembly, typeSymbol.ContainingAssembly)
+ ? typeSymbol.BaseType
+ : null;
+ }
+
+ return false;
+ }
+
+ private bool ShouldRemoveMember(ISymbol? symbol)
+ {
+ if (symbol != null)
+ {
+ INamedTypeSymbol? containingType = symbol.ContainingType;
+ IMethodSymbol? methodSymbol = symbol as IMethodSymbol;
+
+ var suppressions = GetSupressions(symbol.ContainingType);
+ if (suppressions != null)
+ {
+ foreach (var suppression in suppressions)
+ {
+ if (suppression.Matches(symbol))
+ {
+ return true;
+ }
+ }
+ }
+
+ while (containingType != null)
+ {
+ var members = containingType.GetMembers(symbol.Name);
+ foreach (var member in members)
+ {
+ if (!member.Equals(symbol) &&
+ IsDeclaredInNonGeneratedCode(member))
+ {
+ if (methodSymbol != null &&
+ member is IMethodSymbol memberMethodSymbol &&
+ !methodSymbol.Parameters.SequenceEqual(memberMethodSymbol.Parameters, (s1, s2) => s1.Type.Equals(s2.Type)))
+ {
+ continue;
+ }
+
+ return true;
+ }
+ }
+
+ // Skip traversing parents for constructors and explicit interface implementations
+ if (methodSymbol != null &&
+ (methodSymbol.MethodKind == MethodKind.Constructor ||
+ !methodSymbol.ExplicitInterfaceImplementations.IsEmpty))
+ {
+ break;
+ }
+ containingType = containingType.BaseType;
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsDeclaredInNonGeneratedCode(ISymbol member)
+ {
+ var references = member.DeclaringSyntaxReferences;
+
+ if (references.Length == 0)
+ {
+ return false;
+ }
+
+ foreach (var reference in references)
+ {
+ Document? document = _project.GetDocument(reference.SyntaxTree);
+
+ if (document != null && GeneratedCodeWorkspace.IsGeneratedDocument(document))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private readonly struct Supression
+ {
+ private readonly string? _name;
+ private readonly ISymbol?[] _types;
+
+ public Supression(string? name, ISymbol?[] types)
+ {
+ _name = name;
+ _types = types;
+ }
+
+ public bool Matches(ISymbol symbol)
+ {
+ if (symbol is IMethodSymbol methodSymbol)
+ {
+ string name = methodSymbol.Name;
+ // Use friendly name for ctors
+ if (methodSymbol.MethodKind == MethodKind.Constructor)
+ {
+ name = methodSymbol.ContainingType.Name;
+ }
+
+ return _name == name &&
+ _types.SequenceEqual(methodSymbol.Parameters.Select(p => p.Type), SymbolEqualityComparer.Default);
+ }
+ else
+ {
+ return symbol.Name == _name;
+ }
+ }
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Plugins/NewProjectScaffolding.cs b/logger/autorest.csharp/common/AutoRest/Plugins/NewProjectScaffolding.cs
new file mode 100644
index 0000000..1ddf303
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Plugins/NewProjectScaffolding.cs
@@ -0,0 +1,510 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using AutoRest.CSharp.Common.Input;
+using AutoRest.CSharp.Generation.Writers;
+
+namespace AutoRest.CSharp.Common.AutoRest.Plugins
+{
+ internal class NewProjectScaffolding
+ {
+ private string _serviceDirectoryName;
+ private string _projectDirectory;
+ private string _testDirectory;
+ private string _serviceDirectory;
+ private bool _isAzureSdk;
+ private bool _needAzureKeyAuth;
+ private bool _includeDfe;
+
+ public NewProjectScaffolding(bool needAzureKeyAuth, bool includeDfe)
+ {
+ _serviceDirectoryName = Path.GetFileName(Path.GetFullPath(Path.Combine(Configuration.AbsoluteProjectFolder, "..", "..")));
+ _projectDirectory = Path.Combine(Configuration.AbsoluteProjectFolder, "..");
+ _testDirectory = Path.Combine(Configuration.AbsoluteProjectFolder, "..", "tests");
+ _serviceDirectory = Path.Combine(Configuration.AbsoluteProjectFolder, "..", "..");
+ _isAzureSdk = Configuration.Namespace.StartsWith("Azure.");
+ _needAzureKeyAuth = needAzureKeyAuth;
+ _includeDfe = includeDfe;
+ }
+
+ public async Task Execute()
+ {
+ if (!_isAzureSdk)
+ {
+ //clean up old sln and csproj files
+ foreach (var file in Directory.GetFiles(_projectDirectory, "*.csproj", SearchOption.AllDirectories))
+ {
+ File.Delete(file);
+ }
+ foreach (var file in Directory.GetFiles(_projectDirectory, "*.sln", SearchOption.AllDirectories))
+ {
+ File.Delete(file);
+ }
+ }
+
+ if (_isAzureSdk)
+ await WriteServiceDirectoryFiles();
+
+ await WriteSolutionFiles();
+
+ await WriteProjectFiles();
+
+ await WriteTestFiles();
+
+ return true;
+ }
+
+ private async Task WriteServiceDirectoryFiles()
+ {
+ //TODO handle existing ci where multiple projects are in the same service directory
+ string ciYmlFile = Path.Combine(_serviceDirectory, "ci.yml");
+ if (!File.Exists(ciYmlFile))
+ await File.WriteAllBytesAsync(ciYmlFile, Encoding.ASCII.GetBytes(GetCiYml()));
+ }
+
+ private async Task WriteTestFiles()
+ {
+ if (!Configuration.GenerateTestProject && !Configuration.GenerateSampleProject)
+ return;
+
+ if (_isAzureSdk)
+ {
+ Directory.CreateDirectory(Path.Combine(_testDirectory, "SessionRecords"));
+ }
+ if (!Directory.Exists(_testDirectory))
+ Directory.CreateDirectory(_testDirectory);
+
+ await File.WriteAllBytesAsync(Path.Combine(_testDirectory, $"{Configuration.Namespace}.Tests.csproj"), Encoding.ASCII.GetBytes(GetTestCSProj()));
+ }
+
+ private async Task WriteProjectFiles()
+ {
+ await File.WriteAllBytesAsync(Path.Combine(Configuration.AbsoluteProjectFolder, $"{Configuration.Namespace}.csproj"), Encoding.ASCII.GetBytes(GetSrcCSProj()));
+ Directory.CreateDirectory(Path.Combine(Configuration.AbsoluteProjectFolder, "Properties"));
+ await File.WriteAllBytesAsync(Path.Combine(Configuration.AbsoluteProjectFolder, "Properties", "AssemblyInfo.cs"), Encoding.ASCII.GetBytes(GetAssemblyInfo()));
+ }
+
+ private async Task WriteSolutionFiles()
+ {
+ await File.WriteAllBytesAsync(Path.Combine(_projectDirectory, $"{Configuration.Namespace}.sln"), Encoding.ASCII.GetBytes(GetSln()));
+ if (_isAzureSdk)
+ {
+ await File.WriteAllBytesAsync(Path.Combine(_projectDirectory, "Directory.Build.props"), Encoding.ASCII.GetBytes(GetDirectoryBuildProps()));
+ await File.WriteAllBytesAsync(Path.Combine(_projectDirectory, "README.md"), Encoding.ASCII.GetBytes(GetReadme()));
+ await File.WriteAllBytesAsync(Path.Combine(_projectDirectory, "CHANGELOG.md"), Encoding.ASCII.GetBytes(GetChangeLog()));
+ }
+ }
+
+ private string GetAssemblyInfo()
+ {
+ const string publicKey = ", PublicKey = 0024000004800000940000000602000000240000525341310004000001000100d15ddcb29688295338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593daa7b11b4";
+ const string assemblyInfoContent = @"// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo(""{0}.Tests{1}"")]
+{2}";
+ const string azureResourceProvider = @"
+// Replace Microsoft.Test with the correct resource provider namepace for your service and uncomment.
+// See https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/azure-services-resource-providers
+// for the list of possible values.
+[assembly: Azure.Core.AzureResourceProviderNamespace(""Microsoft.Template"")]
+";
+ return string.Format(assemblyInfoContent, Configuration.Namespace, Configuration.IsBranded ? publicKey : string.Empty, _isAzureSdk ? azureResourceProvider : string.Empty);
+ }
+
+ private string GetChangeLog()
+ {
+ const string changeLogContent = @"# Release History
+
+## 1.0.0-beta.1 (Unreleased)
+
+### Features Added
+
+### Breaking Changes
+
+### Bugs Fixed
+
+### Other Changes
+";
+ return changeLogContent;
+ }
+
+ private string GetReadme()
+ {
+ const string multipleApiVersionContent = @"
+
+### Service API versions
+
+The client library targets the latest service API version by default. A client instance accepts an optional service API version parameter from its options to specify which API version service to communicate.
+
+#### Select a service API version
+
+You have the flexibility to explicitly select a supported service API version when instantiating a client by configuring its associated options. This ensures that the client can communicate with services using the specified API version.
+
+For example,
+
+```C# Snippet:CreateClientForSpecificApiVersion
+Uri endpoint = new Uri("""");
+DefaultAzureCredential credential = new DefaultAzureCredential();
+ClientOptions options = new ClientOptions(ClientOptions.ServiceVersion.)
+var client = new Client(endpoint, credential, options);
+```
+
+When selecting an API version, it's important to verify that there are no breaking changes compared to the latest API version. If there are significant differences, API calls may fail due to incompatibility.
+
+Always ensure that the chosen API version is fully supported and operational for your specific use case and that it aligns with the service's versioning policy.";
+ const string readmeContent = @"# {0} client library for .NET
+
+{0} is a managed service that helps developers get secret simply and securely.
+
+Use the client library for to:
+
+* [Get secret](https://docs.microsoft.com/azure)
+
+[Source code][source_root] | [Package (NuGet)][package] | [API reference documentation][reference_docs] | [Product documentation][azconfig_docs] | [Samples][source_samples]
+
+ [Source code](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/{1}/{0}/src) | [Package (NuGet)](https://www.nuget.org/packages) | [API reference documentation](https://azure.github.io/azure-sdk-for-net) | [Product documentation](https://docs.microsoft.com/azure)
+
+## Getting started
+
+This section should include everything a developer needs to do to install and create their first client connection *very quickly*.
+
+### Install the package
+
+First, provide instruction for obtaining and installing the package or library. This section might include only a single line of code, like `dotnet add package package-name`, but should enable a developer to successfully install the package from NuGet, npm, or even cloning a GitHub repository.
+
+Install the client library for .NET with [NuGet](https://www.nuget.org/ ):
+
+```dotnetcli
+dotnet add package {0} --prerelease
+```
+
+### Prerequisites
+
+Include a section after the install command that details any requirements that must be satisfied before a developer can [authenticate](#authenticate-the-client) and test all of the snippets in the [Examples](#examples) section. For example, for Cosmos DB:
+
+> You must have an [Azure subscription](https://azure.microsoft.com/free/dotnet/) and [Cosmos DB account](https://docs.microsoft.com/azure/cosmos-db/account-overview) (SQL API). In order to take advantage of the C# 8.0 syntax, it is recommended that you compile using the [.NET Core SDK](https://dotnet.microsoft.com/download) 3.0 or higher with a [language version](https://docs.microsoft.com/dotnet/csharp/language-reference/configure-language-version#override-a-default) of `latest`. It is also possible to compile with the .NET Core SDK 2.1.x using a language version of `preview`.
+
+### Authenticate the client
+
+If your library requires authentication for use, such as for Azure services, include instructions and example code needed for initializing and authenticating.
+
+For example, include details on obtaining an account key and endpoint URI, setting environment variables for each, and initializing the client object.{2}
+
+## Key concepts
+
+The *Key concepts* section should describe the functionality of the main classes. Point out the most important and useful classes in the package (with links to their reference pages) and explain how those classes work together. Feel free to use bulleted lists, tables, code blocks, or even diagrams for clarity.
+
+Include the *Thread safety* and *Additional concepts* sections below at the end of your *Key concepts* section. You may remove or add links depending on what your library makes use of:
+
+### Thread safety
+
+We guarantee that all client instance methods are thread-safe and independent of each other ([guideline](https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-service-methods-thread-safety)). This ensures that the recommendation of reusing client instances is always safe, even across threads.
+
+### Additional concepts
+
+[Client options](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/README.md#configuring-service-clients-using-clientoptions) |
+[Accessing the response](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/README.md#accessing-http-response-details-using-responset) |
+[Long-running operations](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/README.md#consuming-long-running-operations-using-operationt) |
+[Handling failures](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/README.md#reporting-errors-requestfailedexception) |
+[Diagnostics](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/samples/Diagnostics.md) |
+[Mocking](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/README.md#mocking) |
+[Client lifetime](https://devblogs.microsoft.com/azure-sdk/lifetime-management-and-thread-safety-guarantees-of-azure-sdk-net-clients/)
+
+
+## Examples
+
+You can familiarize yourself with different APIs using [Samples](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/{1}/{0}/samples).
+
+## Troubleshooting
+
+Describe common errors and exceptions, how to ""unpack"" them if necessary, and include guidance for graceful handling and recovery.
+
+Provide information to help developers avoid throttling or other service-enforced errors they might encounter. For example, provide guidance and examples for using retry or connection policies in the API.
+
+If the package or a related package supports it, include tips for logging or enabling instrumentation to help them debug their code.
+
+## Next steps
+
+* Provide a link to additional code examples, ideally to those sitting alongside the README in the package's `/samples` directory.
+* If appropriate, point users to other packages that might be useful.
+* If you think there's a good chance that developers might stumble across your package in error (because they're searching for specific functionality and mistakenly think the package provides that functionality), point them to the packages they might be looking for.
+
+## Contributing
+
+This is a template, but your SDK readme should include details on how to contribute code to the repo/package.
+
+
+[style-guide-msft]: https://docs.microsoft.com/style-guide/capitalization
+[style-guide-cloud]: https://aka.ms/azsdk/cloud-style-guide
+
+
+";
+ return string.Format(readmeContent, Configuration.Namespace, _serviceDirectoryName, (Configuration.AzureArm || Configuration.Generation1ConvenienceClient) ? "" : multipleApiVersionContent);
+ }
+
+ private string GetCiYml()
+ {
+ string safeName = Configuration.Namespace.Replace(".", "");
+ const string ciYmlContent = @"# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file.
+
+trigger:
+ branches:
+ include:
+ - main
+ - hotfix/*
+ - release/*
+ paths:
+ include:
+ - sdk/{0}
+ - sdk/{0}/ci.yml
+ - sdk/{0}/{1}
+
+pr:
+ branches:
+ include:
+ - main
+ - feature/*
+ - hotfix/*
+ - release/*
+ paths:
+ include:
+ - sdk/{0}
+ - sdk/{0}/ci.yml
+ - sdk/{0}/{1}
+
+extends:
+ template: /eng/pipelines/templates/stages/archetype-sdk-client.yml
+ parameters:
+ ServiceDirectory: {0}
+ ArtifactName: packages
+ Artifacts:
+ - name: {1}
+ safeName: {2}
+";
+ return string.Format(ciYmlContent, _serviceDirectoryName, Configuration.Namespace, safeName);
+ }
+
+ private string GetDirectoryBuildProps()
+ {
+ const string directoryBuildPropsContent = @"
+
+
+
+";
+ return directoryBuildPropsContent;
+ }
+
+ private string GetBrandedSrcCSProj()
+ {
+ var builder = new CSProjWriter()
+ {
+ Description = $"This is the {Configuration.Namespace} client library for developing .NET applications with rich experience.",
+ AssemblyTitle = $"Azure SDK Code Generation {Configuration.Namespace} for Azure Data Plane",
+ Version = "1.0.0-beta.1",
+ PackageTags = Configuration.Namespace,
+ TargetFrameworks = "$(RequiredTargetFrameworks)",
+ IncludeOperationsSharedSource = true,
+ };
+ if (_needAzureKeyAuth)
+ builder.CompileIncludes.Add(new("$(AzureCoreSharedSources)AzureKeyCredentialPolicy.cs", "Shared/Core"));
+ if (!Configuration.AzureArm)
+ {
+ // only branded library will add these shared code compilation lines
+ builder.CompileIncludes.Add(new("$(AzureCoreSharedSources)AzureResourceProviderNamespaceAttribute.cs", "Shared/Core"));
+ foreach (var packages in _brandedDependencyPackages)
+ {
+ builder.PackageReferences.Add(packages);
+ }
+ }
+
+ if (_includeDfe)
+ {
+ builder.PackageReferences.Add(new("Azure.Core.Expressions.DataFactory"));
+ }
+
+ return builder.Write();
+ }
+
+ private string GetUnbrandedSrcCSProj()
+ {
+ var builder = new CSProjWriter()
+ {
+ Description = $"This is the {Configuration.Namespace} client library for developing .NET applications with rich experience.",
+ AssemblyTitle = $"SDK Code Generation {Configuration.Namespace}",
+ Version = "1.0.0-beta.1",
+ PackageTags = Configuration.Namespace,
+ TargetFramework = "netstandard2.0",
+ LangVersion = "latest",
+ GenerateDocumentationFile = true,
+ };
+ foreach (var packages in _unbrandedDependencyPackages)
+ {
+ builder.PackageReferences.Add(packages);
+ }
+
+ return builder.Write();
+ }
+
+ private string GetSrcCSProj() => Configuration.IsBranded ? GetBrandedSrcCSProj() : GetUnbrandedSrcCSProj();
+
+ private static readonly IReadOnlyList _brandedDependencyPackages = new CSProjWriter.CSProjDependencyPackage[]
+ {
+ new("Azure.Core"),
+ new("System.Text.Json")
+ };
+ private static readonly IReadOnlyList _unbrandedDependencyPackages = new CSProjWriter.CSProjDependencyPackage[]
+ {
+ new("System.ClientModel", "1.1.0-beta.3"),
+ new("System.Text.Json", "4.7.2")
+ };
+
+ private static readonly IReadOnlyList _brandedTestDependencyPackages = new CSProjWriter.CSProjDependencyPackage[]
+ {
+ new("Azure.Identity"),
+ new("NUnit"),
+ new("NUnit3TestAdapter"),
+ new("Microsoft.NET.Test.Sdk"),
+ new("Moq")
+ };
+ private static readonly IReadOnlyList _unbrandedTestDependencyPackages = new CSProjWriter.CSProjDependencyPackage[]
+ {
+ new("NUnit", "3.13.2"),
+ new("NUnit3TestAdapter", "4.4.2"),
+ new("Microsoft.NET.Test.Sdk", "17.0.0"),
+ new("Moq", "[4.18.2]")
+ };
+
+ private string GetBrandedTestCSProj()
+ {
+ var writer = new CSProjWriter()
+ {
+ TargetFrameworks = "$(RequiredTargetFrameworks)",
+ NoWarn = new("$(NoWarn);CS1591", "We don't care about XML doc comments on test types and members")
+ };
+
+ // add the project references
+ if (_isAzureSdk)
+ {
+ writer.ProjectReferences.Add(new("$(AzureCoreTestFramework)"));
+ }
+ writer.ProjectReferences.Add(new($"..\\src\\{Configuration.Namespace}.csproj"));
+ // add the package references
+ if (!Configuration.AzureArm)
+ {
+ foreach (var package in _brandedTestDependencyPackages)
+ {
+ writer.PackageReferences.Add(package);
+ }
+ }
+
+ return writer.Write();
+ }
+
+ private string GetUnbrandedTestCSProj()
+ {
+ var writer = new CSProjWriter()
+ {
+ TargetFramework = "net8.0",
+ NoWarn = new("$(NoWarn);CS1591", "Ignore XML doc comments on test types and members")
+ };
+
+ // add the project references
+ writer.ProjectReferences.Add(new($"..\\src\\{Configuration.Namespace}.csproj"));
+ // add the package references
+ foreach (var package in _unbrandedTestDependencyPackages)
+ {
+ writer.PackageReferences.Add(package);
+ }
+
+ return writer.Write();
+ }
+
+ private string GetTestCSProj() => Configuration.IsBranded ? GetBrandedTestCSProj() : GetUnbrandedTestCSProj();
+
+ private string GetSln()
+ {
+ string slnContent = @"Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29709.97
+MinimumVisualStudioVersion = 10.0.40219.1
+";
+ if (_isAzureSdk)
+ {
+ slnContent += @"Project(""{{9A19103F-16F7-4668-BE54-9A1E7A4F7556}}"") = ""Azure.Core.TestFramework"", ""..\..\core\Azure.Core.TestFramework\src\Azure.Core.TestFramework.csproj"", ""{{ECC730C1-4AEA-420C-916A-66B19B79E4DC}}""
+EndProject
+";
+ }
+ slnContent += @"Project(""{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}"") = ""{0}"", ""src\{0}.csproj"", ""{{28FF4005-4467-4E36-92E7-DEA27DEB1519}}""
+EndProject
+";
+ if (Configuration.GenerateTestProject || Configuration.GenerateSampleProject)
+ {
+ slnContent += @"Project(""{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}"") = ""{0}.Tests"", ""tests\{0}.Tests.csproj"", ""{{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}}""
+EndProject
+";
+ }
+ slnContent += @"Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {{B0C276D1-2930-4887-B29A-D1A33E7009A2}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {{B0C276D1-2930-4887-B29A-D1A33E7009A2}}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {{B0C276D1-2930-4887-B29A-D1A33E7009A2}}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {{B0C276D1-2930-4887-B29A-D1A33E7009A2}}.Release|Any CPU.Build.0 = Release|Any CPU
+ {{8E9A77AC-792A-4432-8320-ACFD46730401}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {{8E9A77AC-792A-4432-8320-ACFD46730401}}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {{8E9A77AC-792A-4432-8320-ACFD46730401}}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {{8E9A77AC-792A-4432-8320-ACFD46730401}}.Release|Any CPU.Build.0 = Release|Any CPU
+";
+ if (_isAzureSdk)
+ {
+ slnContent += @" {{ECC730C1-4AEA-420C-916A-66B19B79E4DC}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {{ECC730C1-4AEA-420C-916A-66B19B79E4DC}}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {{ECC730C1-4AEA-420C-916A-66B19B79E4DC}}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {{ECC730C1-4AEA-420C-916A-66B19B79E4DC}}.Release|Any CPU.Build.0 = Release|Any CPU
+";
+ }
+ slnContent += @" {{A4241C1F-A53D-474C-9E4E-075054407E74}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {{A4241C1F-A53D-474C-9E4E-075054407E74}}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {{A4241C1F-A53D-474C-9E4E-075054407E74}}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {{A4241C1F-A53D-474C-9E4E-075054407E74}}.Release|Any CPU.Build.0 = Release|Any CPU
+ {{FA8BD3F1-8616-47B6-974C-7576CDF4717E}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {{FA8BD3F1-8616-47B6-974C-7576CDF4717E}}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {{FA8BD3F1-8616-47B6-974C-7576CDF4717E}}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {{FA8BD3F1-8616-47B6-974C-7576CDF4717E}}.Release|Any CPU.Build.0 = Release|Any CPU
+ {{85677AD3-C214-42FA-AE6E-49B956CAC8DC}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {{85677AD3-C214-42FA-AE6E-49B956CAC8DC}}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {{85677AD3-C214-42FA-AE6E-49B956CAC8DC}}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {{85677AD3-C214-42FA-AE6E-49B956CAC8DC}}.Release|Any CPU.Build.0 = Release|Any CPU
+ {{28FF4005-4467-4E36-92E7-DEA27DEB1519}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {{28FF4005-4467-4E36-92E7-DEA27DEB1519}}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {{28FF4005-4467-4E36-92E7-DEA27DEB1519}}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {{28FF4005-4467-4E36-92E7-DEA27DEB1519}}.Release|Any CPU.Build.0 = Release|Any CPU
+ {{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {{A97F4B90-2591-4689-B1F8-5F21FE6D6CAE}}
+ EndGlobalSection
+EndGlobal
+";
+ return string.Format(slnContent, Configuration.Namespace);
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Plugins/PluginProcessor.cs b/logger/autorest.csharp/common/AutoRest/Plugins/PluginProcessor.cs
new file mode 100644
index 0000000..1d0b44d
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Plugins/PluginProcessor.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text.Json;
+using System.Threading.Tasks;
+using AutoRest.CSharp.AutoRest.Communication;
+using AutoRest.CSharp.Common.Input;
+using AutoRest.CSharp.Common.Utilities;
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.AutoRest.Plugins
+{
+ internal static class PluginProcessor
+ {
+ //https://stackoverflow.com/a/26750/294804
+ private static readonly Type[] PluginTypes = Assembly.GetExecutingAssembly().GetTypes()
+ .Where(t => t.Namespace == typeof(IPlugin).Namespace && t.IsClass && !t.IsAbstract && typeof(IPlugin).IsAssignableFrom(t) && t.GetCustomAttribute(true) != null)
+ .ToArray();
+ public static readonly Dictionary> Plugins = PluginTypes
+ .ToDictionary(pt => pt.GetCustomAttribute(true)!.PluginName, pt => (Func)(() => (IPlugin)Activator.CreateInstance(pt)!));
+ public static readonly string[] PluginNames = Plugins.Keys.ToArray();
+
+ public static async Task Start(IPluginCommunication autoRest)
+ {
+ try
+ {
+ IPlugin plugin = Plugins[autoRest.PluginName]();
+ var shouldAttach = await autoRest.GetValue(string.Format(Configuration.Options.AttachDebuggerFormat, autoRest.PluginName));
+ if (shouldAttach.ToBoolean() ?? false)
+ {
+ Console.Error.WriteLine("Attempting to attach debugger.");
+ System.Diagnostics.Debugger.Launch();
+ }
+ AutoRestLogger.Initialize(autoRest);
+ return await plugin.Execute(autoRest);
+ }
+ catch (Exception e)
+ {
+ await autoRest.Fatal(e.ToString());
+ return false;
+ }
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Plugins/SA1505Rewriter.cs b/logger/autorest.csharp/common/AutoRest/Plugins/SA1505Rewriter.cs
new file mode 100644
index 0000000..380ce20
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Plugins/SA1505Rewriter.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace AutoRest.CSharp.Common.AutoRest.Plugins
+{
+ ///
+ /// This Roslyn CSharpSyntaxRewriter will delete extra newlines after the
+ /// open brace of a class/struct/enum definition. It is for resolving SA1505 error.
+ ///
+ internal class SA1505Rewriter : CSharpSyntaxRewriter
+ {
+ public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node)
+ => base.VisitClassDeclaration((ClassDeclarationSyntax)RemoveEOLAfterOpenBrace(node));
+
+ public override SyntaxNode? VisitStructDeclaration(StructDeclarationSyntax node)
+ => base.VisitStructDeclaration((StructDeclarationSyntax)RemoveEOLAfterOpenBrace(node));
+
+ public override SyntaxNode? VisitEnumDeclaration(EnumDeclarationSyntax node)
+ => base.VisitEnumDeclaration((EnumDeclarationSyntax)RemoveEOLAfterOpenBrace(node));
+
+ private BaseTypeDeclarationSyntax RemoveEOLAfterOpenBrace(BaseTypeDeclarationSyntax node)
+ {
+ // all extra EOL after open brace are the leading trivia of the next token
+ var nextToken = node.OpenBraceToken.GetNextToken();
+ if (nextToken.IsMissing)
+ {
+ return node;
+ }
+
+ var leadingTrivia = nextToken.LeadingTrivia;
+ if (leadingTrivia.Count == 0 || !leadingTrivia[0].IsKind(SyntaxKind.EndOfLineTrivia))
+ {
+ return node;
+ }
+
+ var newLeadingTrvia = leadingTrivia.SkipWhile(t => t.IsKind(SyntaxKind.EndOfLineTrivia));
+ var newNextToken = nextToken.WithLeadingTrivia(newLeadingTrvia);
+ return node.ReplaceToken(nextToken, newNextToken);
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Plugins/XmlDocumentFile.cs b/logger/autorest.csharp/common/AutoRest/Plugins/XmlDocumentFile.cs
new file mode 100644
index 0000000..65fbd33
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Plugins/XmlDocumentFile.cs
@@ -0,0 +1,9 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using AutoRest.CSharp.Generation.Writers;
+
+namespace AutoRest.CSharp.AutoRest.Plugins
+{
+ internal record XmlDocumentFile(string TestFileName, XmlDocWriter XmlDocWriter);
+}
diff --git a/logger/autorest.csharp/common/AutoRest/Plugins/XmlFormatter.cs b/logger/autorest.csharp/common/AutoRest/Plugins/XmlFormatter.cs
new file mode 100644
index 0000000..5bf8ffd
--- /dev/null
+++ b/logger/autorest.csharp/common/AutoRest/Plugins/XmlFormatter.cs
@@ -0,0 +1,167 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Linq;
+using AutoRest.CSharp.Generation.Writers;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace AutoRest.CSharp.AutoRest.Plugins
+{
+ internal class XmlFormatter
+ {
+ internal static async Task FormatAsync(XmlDocWriter writer, SyntaxTree syntaxTree)
+ {
+ var document = writer.Document;
+ var methods = await GetMethodsAsync(syntaxTree);
+ // first we need to get the members
+ var members = writer.Members;
+
+ foreach (var member in members)
+ {
+ // get the example element
+ var exampleElement = member.Element("example");
+ if (exampleElement == null)
+ continue;
+
+ foreach (var codeElement in exampleElement.Elements("code"))
+ {
+ var testMethodName = codeElement.Value;
+ // find the magic comment and replace it with real code
+ if (methods.TryGetValue(testMethodName, out var methodDeclaration))
+ {
+ var lines = GetLines(methodDeclaration.Body!);
+ var content = FormatContent(lines);
+ // this will give you
+ // <[[CDATA
+ // var our = code;
+ // ]]>
+ codeElement.ReplaceAll(new XCData(content));
+ }
+ }
+ }
+
+ var swriter = new XmlStringWriter();
+ XmlWriterSettings settings = new XmlWriterSettings { OmitXmlDeclaration = false, Indent = true };
+ using (XmlWriter xw = XmlWriter.Create(swriter, settings))
+ {
+ document.Save(xw);
+ }
+
+ return swriter.ToString();
+ }
+
+ private static string[] GetLines(BlockSyntax methodBlock)
+ {
+ if (!methodBlock.Statements.Any())
+ return Array.Empty();
+
+ // here we have to get the string of all statements and then split by new lines
+ // this is because in the StatementSyntax, the NewLines (\n or \r\n) could appear at the end of the statement or at the beginning of the statement
+ // therefore to keep it simple, we just combine all the text together and then split by the new lines to trim the extra spaces in front of every line
+ var builder = new StringBuilder();
+ foreach (var statement in methodBlock.Statements)
+ {
+ builder.Append(statement.ToFullString());
+ }
+
+ return builder.ToString().Split(Environment.NewLine);
+ }
+
+ ///
+ /// This method trims the leading spaces of the lines, and it also adds proper amount of spaces to the content of spaces because of the bug of Roslyn: https://github.com/dotnet/roslyn/issues/8269
+ ///
+ ///
+ ///
+ internal static string FormatContent(string[] lines)
+ {
+ if (!lines.Any())
+ return string.Empty;
+
+ var builder = new StringBuilder();
+
+ // find the first non-empty line
+ int lineNumber = -1;
+ for (int i = 0; i < lines.Length; i++)
+ {
+ var line = lines[i];
+ if (!string.IsNullOrWhiteSpace(line))
+ {
+ lineNumber = i;
+ break;
+ }
+ if (i > 0)
+ builder.AppendLine(); // we do not need a new line when it is the first line
+ builder.Append(line);
+ }
+
+ if (lineNumber < 0)
+ return string.Empty; // every line is whitespaces
+
+ // the following code is a temporarily workaround the Roslyn's format issue around collection initializers: https://github.com/dotnet/roslyn/issues/8269
+ // if Roslyn could properly format the collection initializers and everything, this code should be as simple as: take a amount of spaces on the first line, trim spaces with the same amount on every line
+ // since the code we are processing here has been formatted by Roslyn, we only take the cases that lines starts or ends with { or } to format.
+ var stack = new Stack();
+ stack.Push(0);
+ const int spaceIncrement = 4;
+
+ for (int i = lineNumber; i < lines.Length; i++)
+ {
+ var line = lines[i].AsSpan();
+ // first we count how many leading spaces we are having on this line
+ int count = 0;
+ while (count < line.Length && char.IsWhiteSpace(line[count]))
+ {
+ count++;
+ }
+ var spaceCount = count;
+ // if the rest part of the line leads by a }, we should decrease the amount of leading spaces
+ if (count < line.Length && line[count] == '}')
+ {
+ stack.Pop();
+ }
+ // find out how many spaces we would like to prepend
+ var leadingSpaces = stack.Peek();
+ // if the rest part of the line leads by a {, we increment the leading space
+ if (count < line.Length && line[count] == '{')
+ {
+ stack.Push(stack.Peek() + spaceIncrement);
+ }
+ builder.AppendLine();
+ while (leadingSpaces > 0)
+ {
+ builder.Append(' ');
+ leadingSpaces--;
+ }
+ builder.Append(line.Slice(spaceCount));
+ }
+
+ return builder.ToString();
+ }
+
+ private static async Task> GetMethodsAsync(SyntaxTree syntaxTree)
+ {
+ var result = new Dictionary();
+ var root = await syntaxTree.GetRootAsync();
+
+ foreach (var method in root.DescendantNodes().OfType())
+ {
+ result.Add(method.Identifier.Text, method);
+ }
+
+ return result;
+ }
+
+ private class XmlStringWriter : StringWriter
+ {
+ public override Encoding Encoding => Encoding.UTF8;
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Decorator/ConstantSchemaTransformer.cs b/logger/autorest.csharp/common/Decorator/ConstantSchemaTransformer.cs
new file mode 100644
index 0000000..59c68a4
--- /dev/null
+++ b/logger/autorest.csharp/common/Decorator/ConstantSchemaTransformer.cs
@@ -0,0 +1,117 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using AutoRest.CSharp.Input;
+
+namespace AutoRest.CSharp.Common.Decorator
+{
+ internal static class ConstantSchemaTransformer
+ {
+ public static void Transform(CodeModel codeModel)
+ {
+ var constantSchemas = new HashSet(codeModel.Schemas.Constants);
+ if (!constantSchemas.Any())
+ return;
+
+ Dictionary convertedChoiceSchemas = new();
+
+ foreach (var operation in codeModel.OperationGroups.SelectMany(og => og.Operations))
+ {
+ // change the schema on operations (only for optional)
+ foreach (var parameter in operation.Parameters)
+ {
+ if (parameter.IsRequired || parameter.Schema is not ConstantSchema constantSchema || ShouldSkipReplace(constantSchema))
+ continue;
+
+ var choiceSchema = ComputeIfAbsent(convertedChoiceSchemas, constantSchema, ConvertToChoiceSchema);
+ constantSchema.ValueType = choiceSchema;
+ operation.SignatureParameters.Add(parameter);
+ }
+
+ foreach (var request in operation.Requests)
+ {
+ foreach (var parameter in request.Parameters)
+ {
+ if (parameter.IsRequired || parameter.Schema is not ConstantSchema constantSchema || ShouldSkipReplace(constantSchema))
+ continue;
+
+ var choiceSchema = ComputeIfAbsent(convertedChoiceSchemas, constantSchema, ConvertToChoiceSchema);
+ constantSchema.ValueType = choiceSchema;
+ request.SignatureParameters.Add(parameter);
+ }
+ }
+
+ // change the schema on models (optional and required)
+ foreach (var obj in codeModel.Schemas.Objects)
+ {
+ foreach (var property in obj.Properties)
+ {
+ if (property.Schema is not ConstantSchema constantSchema || ShouldSkipReplace(constantSchema) || CheckPropertyExtension(property))
+ continue;
+
+ var choiceSchema = ComputeIfAbsent(convertedChoiceSchemas, constantSchema, ConvertToChoiceSchema);
+ constantSchema.ValueType = choiceSchema;
+ }
+ }
+ }
+
+ foreach (var choiceSchema in convertedChoiceSchemas.Values)
+ codeModel.Schemas.Choices.Add(choiceSchema);
+ }
+
+ // we skip this process when the underlying type of the constant is boolean
+ private static bool ShouldSkipReplace(ConstantSchema constantSchema)
+ => constantSchema.ValueType is BooleanSchema;
+
+ private static bool CheckPropertyExtension(Property property)
+ {
+ if (property.Extensions?.TryGetValue("x-ms-constant", out var value) ?? false)
+ {
+ return "true".Equals(value.ToString());
+ }
+ return false;
+ }
+
+ private static V ComputeIfAbsent(Dictionary dict, K key, Func generator) where K : notnull
+ {
+ if (dict.TryGetValue(key, out var value))
+ {
+ return value;
+ }
+ var generated = generator(key);
+ dict.Add(key, generated);
+ return generated;
+ }
+
+ private static ChoiceSchema ConvertToChoiceSchema(ConstantSchema constantSchema)
+ {
+ var choiceValue = constantSchema.Value.Value.ToString();
+ ChoiceValue choice = new()
+ {
+ Value = choiceValue,
+ Language = constantSchema.Value.Language != null ?
+ constantSchema.Value.Language :
+ new Languages
+ {
+ Default = new Language
+ {
+ Name = choiceValue,
+ }
+ }
+ };
+
+ ChoiceSchema choiceSchema = new()
+ {
+ Type = AllSchemaTypes.Choice,
+ ChoiceType = (PrimitiveSchema)constantSchema.ValueType,
+ DefaultValue = constantSchema.DefaultValue,
+ Language = constantSchema.Language,
+ Choices = new[] { choice }
+ };
+ return choiceSchema;
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Decorator/ModelPropertyClientDefaultValueTransformer.cs b/logger/autorest.csharp/common/Decorator/ModelPropertyClientDefaultValueTransformer.cs
new file mode 100644
index 0000000..74818ba
--- /dev/null
+++ b/logger/autorest.csharp/common/Decorator/ModelPropertyClientDefaultValueTransformer.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using AutoRest.CSharp.Input;
+
+namespace AutoRest.CSharp.Common.Decorator
+{
+ ///
+ /// This class is used to eliminate the value of ClientDefaultValue which is defined in the "x-ms-client-default" property attribute.
+ /// We will not generate any client default value into the SDK for model property, so we remove it from the code model.
+ ///
+ internal class ModelPropertyClientDefaultValueTransformer
+ {
+ public static void Transform(CodeModel codeModel)
+ {
+ foreach (var schema in codeModel.AllSchemas.OfType())
+ {
+ foreach (var property in schema.Properties)
+ {
+ /* eliminate the client default value of model property */
+ property.ClientDefaultValue = null;
+ }
+ }
+ }
+
+ }
+}
diff --git a/logger/autorest.csharp/common/Decorator/SchemaUsageTransformer.cs b/logger/autorest.csharp/common/Decorator/SchemaUsageTransformer.cs
new file mode 100644
index 0000000..f55dc79
--- /dev/null
+++ b/logger/autorest.csharp/common/Decorator/SchemaUsageTransformer.cs
@@ -0,0 +1,176 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using AutoRest.CSharp.Input;
+
+namespace AutoRest.CSharp.Common.Decorator
+{
+ ///
+ /// This class is used to transform usages for definitions listed in the "x-ms-format-element-type" property attribute.
+ /// It must be run before the transform (or any transforms that depend on the usages)
+ /// so that the default derived schema logic is working with the correct usages.
+ ///
+ internal static class SchemaUsageTransformer
+ {
+ private const string FormatElementDefinition = "x-ms-format-element-type";
+ private const string CSharpUsage = "x-csharp-usage";
+
+ public static void Transform(CodeModel codeModel)
+ {
+ Dictionary> schemaToPropertyMap = new();
+ Dictionary> schemaToEnclosingSchemasMap = new();
+
+ foreach (var schema in codeModel.AllSchemas.OfType())
+ {
+ foreach (var property in schema.Properties)
+ {
+ if (property.Extensions?.TryGetValue(FormatElementDefinition, out var value) == true)
+ {
+ string valueString = (string)value;
+
+ if (valueString.StartsWith("#/definitions/"))
+ {
+ valueString = valueString.Substring("#/definitions/".Length);
+ }
+
+ if (!schemaToPropertyMap.ContainsKey(valueString))
+ {
+ schemaToPropertyMap.Add(valueString, new List());
+ }
+
+ if (!schemaToEnclosingSchemasMap.ContainsKey(valueString))
+ {
+ schemaToEnclosingSchemasMap.Add(valueString, new List());
+ }
+
+ schemaToPropertyMap[valueString].Add(property);
+ schemaToEnclosingSchemasMap[valueString].Add(schema);
+ }
+ }
+ }
+
+ if (schemaToPropertyMap.Count == 0)
+ return;
+
+ foreach (var schema in codeModel.AllSchemas.OfType())
+ {
+ if (!schemaToPropertyMap.TryGetValue(schema.Name, out var propertyList)) continue;
+
+ schemaToPropertyMap.Remove(schema.Name);
+ foreach (var property in propertyList)
+ {
+ property.Extensions![FormatElementDefinition] = schema;
+ }
+
+ schema.Extensions ??= new RecordOfStringAndAny();
+
+ // apply usages and media types based on the enclosing schemas for the properties that reference
+ // the "x-ms-format-element-type" schema
+
+ HashSet additionalUsages = new();
+ HashSet additionalMediaTypes = new();
+ foreach (var enclosingSchema in schemaToEnclosingSchemasMap[schema.Name])
+ {
+ if (enclosingSchema is ObjectSchema objectSchema)
+ {
+ foreach (SchemaContext schemaUsage in objectSchema.Usage)
+ {
+ if (schemaUsage == SchemaContext.Exception) continue;
+ additionalUsages.Add(schemaUsage == SchemaContext.Input ? "input" : "output");
+ }
+
+ foreach (KnownMediaType mediaType in objectSchema.SerializationFormats)
+ {
+ additionalMediaTypes.Add(mediaType);
+ }
+ }
+ }
+
+ if (additionalUsages.Count > 0)
+ {
+ // This is a hack to avoid needing to update the SchemaUsageProvider logic to look up the property schema using "x-ms-format-element-type"
+ // The problem with doing this here is that we don't know for sure if this should be a public model, but if we don't mark is as a model
+ // here then it will be generated as internal, since it will not necessarily be included in the SchemaUsageProvider logic that
+ // loops through model properties.
+ additionalUsages.Add("model");
+ }
+
+ // apply converter usage to any schemas that are referenced with "x-ms-format-element-type" in a property
+ additionalUsages.Add("converter");
+
+ // recursively apply the usages and media types to the schema and all property schemas on the schema
+ Apply(schema, string.Join(",", additionalUsages), additionalMediaTypes, new HashSet());
+ }
+
+ if (schemaToPropertyMap.Count > 0)
+ {
+ var schemaList = schemaToPropertyMap.Keys.Aggregate((a, b) => $"{a}, {b}");
+ throw new InvalidOperationException($"The following schemas were referenced by properties with the '{FormatElementDefinition}' attribute, but were not found in any definitions: " + schemaList);
+ }
+ }
+
+ private static void Apply(ObjectSchema schema, string usages, HashSet mediaTypes, HashSet appliedSchemas)
+ {
+ if (appliedSchemas.Contains(schema))
+ return;
+
+ appliedSchemas.Add(schema);
+
+
+ schema.Extensions ??= new RecordOfStringAndAny();
+ if (!schema.Extensions!.TryGetValue(CSharpUsage, out var existingUsages))
+ {
+ schema.Extensions.Add(CSharpUsage, usages);
+ }
+ else
+ {
+ if (existingUsages is string usage && !string.IsNullOrEmpty(usage))
+ {
+ schema.Extensions![CSharpUsage] = usage + "," + usages;
+ }
+ else
+ {
+ schema.Extensions![CSharpUsage] = usages;
+ }
+ }
+
+ foreach (var mediaType in mediaTypes)
+ {
+ schema.SerializationFormats.Add(mediaType);
+ }
+
+ foreach (var property in schema.Properties)
+ {
+ if (property.Schema is ObjectSchema propertySchema)
+ {
+ Apply(propertySchema, usages, mediaTypes, appliedSchemas);
+ }
+ }
+
+ if (schema.Children != null)
+ {
+ foreach (var child in schema.Children!.Immediate)
+ {
+ if (child is ObjectSchema propertySchema)
+ {
+ Apply(propertySchema, usages, mediaTypes, appliedSchemas);
+ }
+ }
+ }
+
+ if (schema.Parents != null)
+ {
+ foreach (var parent in schema.Parents!.Immediate)
+ {
+ if (parent is ObjectSchema propertySchema)
+ {
+ Apply(propertySchema, usages, mediaTypes, appliedSchemas);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Types/CSharpType.cs b/logger/autorest.csharp/common/Generation/Types/CSharpType.cs
new file mode 100644
index 0000000..4ccf7d8
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Types/CSharpType.cs
@@ -0,0 +1,681 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using AutoRest.CSharp.Generation.Writers;
+using AutoRest.CSharp.Input.Source;
+using AutoRest.CSharp.Output.Models;
+using AutoRest.CSharp.Output.Models.Shared;
+using AutoRest.CSharp.Output.Models.Types;
+using Azure;
+
+namespace AutoRest.CSharp.Generation.Types
+{
+ ///
+ /// CSharpType represents the C# type of an input type.
+ /// It is constructed from a and its properties.
+ ///
+ public class CSharpType
+ {
+ private readonly TypeProvider? _implementation;
+ private readonly Type? _type;
+ private string _name;
+ private string _namespace;
+ private CSharpType? _declaringType;
+ private bool _isValueType;
+ private bool _isEnum;
+ private bool _isNullable;
+ private bool _isPublic;
+ private bool? _isUnion;
+ private IReadOnlyList _arguments;
+ private IReadOnlyList _unionItemTypes;
+
+ private bool? _isReadOnlyMemory;
+ private bool? _isList;
+ private bool? _isArray;
+ private bool? _isReadOnlyList;
+ private bool? _isReadWriteList;
+ private bool? _isDictionary;
+ private bool? _isReadOnlyDictionary;
+ private bool? _isReadWriteDictionary;
+ private bool? _isCollection;
+ private bool? _isIEnumerableOfT;
+ private bool? _isIAsyncEnumerableOfT;
+ private bool? _isResponse;
+ private bool? _isResponseOfT;
+ private bool? _isPageable;
+ private bool? _isAsyncPageable;
+ private bool? _isOperation;
+ private bool? _isOperationOfT;
+ private bool? _isOperationOfAsyncPageable;
+ private bool? _isOperationOfPageable;
+ private int? _hashCode;
+ private CSharpType? _initializationType;
+ private CSharpType? _propertyInitializationType;
+ private CSharpType? _elementType;
+ private CSharpType? _inputType;
+ private CSharpType? _outputType;
+ internal bool IsReadOnlyMemory => _isReadOnlyMemory ??= TypeIsReadOnlyMemory();
+ internal bool IsList => _isList ??= TypeIsList();
+ internal bool IsArray => _isArray ??= TypeIsArray();
+ internal bool IsReadOnlyList => _isReadOnlyList ??= TypeIsReadOnlyList();
+ internal bool IsReadWriteList => _isReadWriteList ??= TypeIsReadWriteList();
+ internal bool IsDictionary => _isDictionary ??= TypeIsDictionary();
+ internal bool IsReadOnlyDictionary => _isReadOnlyDictionary ??= TypeIsReadOnlyDictionary();
+ internal bool IsReadWriteDictionary => _isReadWriteDictionary ??= TypeIsReadWriteDictionary();
+ internal bool IsIEnumerableOfT => _isIEnumerableOfT ??= TypeIsIEnumerableOfT();
+ internal bool IsIAsyncEnumerableOfT => _isIAsyncEnumerableOfT ??= TypeIsIAsyncEnumerableOfT();
+
+ #region AzureTypes
+ // TODO -- these are azure-specific. We might need to generalize these using ApiTypes
+ internal bool IsResponse => _isResponse ??= TypeIsResponse();
+ internal bool IsResponseOfT => _isResponseOfT ??= TypeIsResponseOfT();
+ internal bool IsPageable => _isPageable ??= TypeIsPageable();
+ internal bool IsAsyncPageable => _isAsyncPageable ??= TypeIsAsyncPageable();
+ internal bool IsOperation => _isOperation ??= TypeIsOperation();
+ internal bool IsOperationOfT => _isOperationOfT ??= TypeIsOperationOfT();
+ internal bool IsOperationOfAsyncPageable => _isOperationOfAsyncPageable ??= TypeIsOperationOfAsyncPageable();
+ internal bool IsOperationOfPageable => _isOperationOfPageable ??= TypeIsOperationOfPageable();
+ #endregion
+
+ ///
+ /// Constructs a from a .
+ ///
+ /// The base system type.
+ /// Optional flag to determine if the constructed type should be nullable. Defaults to false.
+ public CSharpType(Type type, bool isNullable = false) : this(
+ type,
+ isNullable,
+ type.IsGenericType ? type.GetGenericArguments().Select(p => new CSharpType(p)).ToArray() : Array.Empty())
+ { }
+
+ ///
+ /// Constructs a non-nullable from a with arguments
+ ///
+ /// The base system type.
+ /// The type's arguments.
+ public CSharpType(Type type, params CSharpType[] arguments) : this(type, arguments, false)
+ { }
+
+ ///
+ /// Constructs a from a with arguments.
+ ///
+ /// The base system type.
+ /// Optional flag to determine if the constructed type should be nullable. Defaults to false.
+ /// The type's arguments.
+ public CSharpType(Type type, bool isNullable, params CSharpType[] arguments) : this(type, arguments, isNullable)
+ { }
+
+ ///
+ /// Constructs a from a .
+ ///
+ /// The base system type.
+ /// The type's arguments.
+ /// Optional flag to determine if the constructed type should be nullable. Defaults to false.
+ public CSharpType(Type type, IReadOnlyList arguments, bool isNullable = false)
+ {
+ Debug.Assert(type.Namespace != null, "type.Namespace != null");
+
+ // handle nullable value types explicitly because they are implemented using generic type `System.Nullable`
+ var underlyingValueType = Nullable.GetUnderlyingType(type);
+ if (underlyingValueType != null)
+ {
+ // in this block, we are converting input like `typeof(int?)` into the way as if they input: `typeof(int), isNullable: true`
+ type = underlyingValueType;
+ arguments = type.IsGenericType ? type.GetGenericArguments().Select(p => new CSharpType(p)).ToArray() : Array.Empty();
+ isNullable = true;
+ }
+
+ _type = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
+ ValidateArguments(_type, arguments);
+
+ var name = type.IsGenericType ? type.Name.Substring(0, type.Name.IndexOf('`')) : type.Name;
+ var isValueType = type.IsValueType;
+ var isEnum = type.IsEnum;
+ var ns = type.Namespace;
+ var isPublic = type.IsPublic && arguments.All(t => t.IsPublic);
+ // open generic parameter such as the `T` in `List` is considered as declared inside the `List` type as well, but we just want this to be the pure nested type, therefore here we exclude the open generic parameter scenario
+ // for a closed generic parameter such as the `string` in `List`, it is just an ordinary type without a `DeclaringType`.
+ var declaringType = type.DeclaringType is not null && !type.IsGenericParameter ? new CSharpType(type.DeclaringType) : null;
+
+ Initialize(name, isValueType, isEnum, isNullable, ns, declaringType, arguments, isPublic);
+ }
+
+ [Conditional("DEBUG")]
+ private static void ValidateArguments(Type type, IReadOnlyList arguments)
+ {
+ if (type.IsGenericTypeDefinition)
+ {
+ Debug.Assert(arguments.Count == type.GetGenericArguments().Length, $"the count of arguments given ({string.Join(", ", arguments.Select(a => a.ToString()))}) does not match the arguments in the definition {type}");
+ }
+ else
+ {
+ Debug.Assert(arguments.Count == 0, "arguments can be added only to the generic type definition.");
+ }
+ }
+
+ internal CSharpType(TypeProvider implementation, IReadOnlyList? arguments = null, bool isNullable = false)
+ {
+ _implementation = implementation;
+ _arguments = arguments ?? Array.Empty();
+
+ var isPublic = implementation is ExpressionTypeProvider expressionTypeProvider
+ ? expressionTypeProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Public)
+ : implementation.Declaration.Accessibility == "public";
+
+ isPublic = isPublic && Arguments.All(t => t.IsPublic);
+
+ var ns = implementation.Declaration.Namespace;
+ var name = implementation.Declaration.Name;
+ var declaringType = implementation.DeclaringTypeProvider?.Type;
+ var isValueType = implementation.IsValueType;
+ var isEnum = implementation.IsEnum;
+
+ Initialize(name, isValueType, isEnum, isNullable, ns, declaringType, arguments, isPublic);
+
+ SerializeAs = _implementation?.SerializeAs;
+ }
+
+ [MemberNotNull(nameof(_name))]
+ [MemberNotNull(nameof(_isValueType))]
+ [MemberNotNull(nameof(_isEnum))]
+ [MemberNotNull(nameof(_isNullable))]
+ [MemberNotNull(nameof(_namespace))]
+ [MemberNotNull(nameof(_arguments))]
+ [MemberNotNull(nameof(_isPublic))]
+ [MemberNotNull(nameof(_unionItemTypes))]
+ private void Initialize(string? name, bool isValueType, bool isEnum, bool isNullable, string? ns,
+ CSharpType? declaringType, IReadOnlyList? args, bool isPublic)
+ {
+ _name = name ?? string.Empty;
+ _isValueType = isValueType;
+ _isEnum = isEnum;
+ _isNullable = isNullable;
+ _namespace = ns ?? string.Empty;
+ _declaringType = declaringType;
+ _arguments = args ?? Array.Empty();
+ _isPublic = isPublic;
+ _unionItemTypes ??= Array.Empty();
+ }
+
+ public virtual string Namespace { get { return _namespace; } }
+ public virtual string Name { get { return _name; } }
+ public CSharpType? DeclaringType { get { return _declaringType; } }
+ public bool IsValueType { get { return _isValueType; } }
+ public bool IsEnum { get { return _isEnum; } }
+ public bool IsLiteral => Literal is not null;
+ public bool IsUnion => _isUnion ??= UnionItemTypes.Count > 0;
+ public bool IsPublic { get { return _isPublic; } }
+ public bool IsFrameworkType => _type != null;
+ public bool IsNullable { get { return _isNullable; } }
+ public bool IsGenericType => Arguments.Count > 0;
+ public bool IsCollection => _isCollection ??= TypeIsCollection();
+ public Type FrameworkType => _type ?? throw new InvalidOperationException("Not a framework type");
+ internal Constant? Literal { get; private init; }
+ internal TypeProvider Implementation => _implementation ?? throw new InvalidOperationException($"Not implemented type: '{Namespace}.{Name}'");
+ public IReadOnlyList Arguments { get { return _arguments; } }
+ public CSharpType InitializationType => _initializationType ??= GetImplementationType();
+ public CSharpType PropertyInitializationType => _propertyInitializationType ??= GetPropertyImplementationType();
+ public CSharpType ElementType => _elementType ??= GetElementType();
+ public CSharpType InputType => _inputType ??= GetInputType();
+ public CSharpType OutputType => _outputType ??= GetOutputType();
+ public Type? SerializeAs { get; init; }
+ public IReadOnlyList UnionItemTypes { get { return _unionItemTypes; } private init { _unionItemTypes = value; } }
+
+ private bool TypeIsReadOnlyMemory()
+ => IsFrameworkType && _type == typeof(ReadOnlyMemory<>);
+
+ private bool TypeIsReadOnlyList()
+ => IsFrameworkType && (_type == typeof(IEnumerable<>) || _type == typeof(IReadOnlyList<>));
+
+ private bool TypeIsReadWriteList()
+ => IsFrameworkType && (_type == typeof(IList<>) || _type == typeof(ICollection<>) || _type == typeof(List<>));
+
+ private bool TypeIsList()
+ => IsReadOnlyList || IsReadWriteList || IsReadOnlyMemory;
+
+ private bool TypeIsArray()
+ => IsFrameworkType && FrameworkType.IsArray;
+
+ private bool TypeIsReadOnlyDictionary()
+ => IsFrameworkType && _type == typeof(IReadOnlyDictionary<,>);
+
+ private bool TypeIsReadWriteDictionary()
+ => IsFrameworkType && (_type == typeof(IDictionary<,>));
+
+ private bool TypeIsDictionary()
+ => IsReadOnlyDictionary || IsReadWriteDictionary;
+
+ private bool TypeIsCollection()
+ => IsFrameworkType && (IsDictionary || IsList);
+
+ ///
+ /// Retrieves the implementation type for the .
+ ///
+ /// The implementation type .
+ private CSharpType GetImplementationType()
+ {
+ if (IsFrameworkType)
+ {
+ if (IsReadOnlyMemory)
+ {
+ return new CSharpType(Arguments[0].FrameworkType.MakeArrayType());
+ }
+
+ if (IsList)
+ {
+ return new CSharpType(typeof(List<>), Arguments);
+ }
+
+ if (IsDictionary)
+ {
+ return new CSharpType(typeof(Dictionary<,>), Arguments);
+ }
+ }
+
+ return this;
+ }
+
+ ///
+ /// Retrieves the implementation type for the .
+ ///
+ /// The implementation type .
+ private CSharpType GetPropertyImplementationType()
+ {
+ if (IsFrameworkType)
+ {
+ if (IsReadOnlyMemory)
+ {
+ return new CSharpType(typeof(ReadOnlyMemory<>), Arguments);
+ }
+
+ if (IsList)
+ {
+ return new CSharpType(ChangeTrackingListProvider.Instance, Arguments);
+ }
+
+ if (IsDictionary)
+ {
+ return new CSharpType(ChangeTrackingDictionaryProvider.Instance, Arguments);
+ }
+ }
+
+ return this;
+ }
+
+ ///
+ /// Retrieves the element type for the . If the type is not an array, list, or dictionary, an exception is thrown.
+ ///
+ /// The element type for the .
+ /// Thrown when the type is not a framework type, array, list, or dictionary.
+ private CSharpType GetElementType()
+ {
+ if (IsFrameworkType)
+ {
+ if (FrameworkType.IsArray)
+ {
+ return new CSharpType(FrameworkType.GetElementType()!);
+ }
+
+ if (IsReadOnlyMemory || IsList)
+ {
+ return Arguments[0];
+ }
+
+ if (IsDictionary)
+ {
+ return Arguments[1];
+ }
+ }
+
+ throw new NotSupportedException(Name);
+ }
+
+ ///
+ /// Retrieves the input type for the .
+ ///
+ /// The input type.
+ private CSharpType GetInputType()
+ {
+ if (IsFrameworkType)
+ {
+ if (IsReadOnlyMemory)
+ {
+ return new CSharpType(typeof(ReadOnlyMemory<>), isNullable: IsNullable, arguments: Arguments);
+ }
+
+ if (IsList)
+ {
+ return new CSharpType(
+ typeof(IEnumerable<>),
+ isNullable: IsNullable,
+ arguments: Arguments);
+ }
+ }
+
+ return this;
+ }
+
+ ///
+ /// Retrieves the output type for the .
+ ///
+ /// The output type.
+ private CSharpType GetOutputType()
+ {
+ if (IsFrameworkType)
+ {
+ if (IsReadOnlyMemory)
+ {
+ return new CSharpType(typeof(ReadOnlyMemory<>), isNullable: IsNullable, arguments: Arguments);
+ }
+
+ if (IsList)
+ {
+ return new CSharpType(
+ typeof(IReadOnlyList<>),
+ isNullable: IsNullable,
+ arguments: Arguments);
+ }
+
+ if (IsDictionary)
+ {
+ return new CSharpType(
+ typeof(IReadOnlyDictionary<,>),
+ isNullable: IsNullable,
+ arguments: Arguments);
+ }
+ }
+
+ return this;
+ }
+
+ private bool TypeIsIEnumerableOfT() => IsFrameworkType && FrameworkType == typeof(IEnumerable<>);
+
+ private bool TypeIsIAsyncEnumerableOfT() => IsFrameworkType && FrameworkType == typeof(IAsyncEnumerable<>);
+
+ private bool TypeIsResponse() => IsFrameworkType && FrameworkType == typeof(Response);
+
+ private bool TypeIsResponseOfT() => IsFrameworkType && FrameworkType == typeof(Response<>);
+
+ private bool TypeIsPageable() => IsFrameworkType && FrameworkType == typeof(Pageable<>);
+
+ private bool TypeIsAsyncPageable() => IsFrameworkType && FrameworkType == typeof(AsyncPageable<>);
+
+ private bool TypeIsOperation() => IsFrameworkType && FrameworkType == typeof(Operation);
+
+ private bool TypeIsOperationOfT() => IsFrameworkType && FrameworkType == typeof(Operation<>);
+
+ private bool TypeIsOperationOfAsyncPageable() => IsOperationOfT && Arguments.Count == 1 && Arguments[0].IsAsyncPageable;
+
+ private bool TypeIsOperationOfPageable() => IsOperationOfT && Arguments.Count == 1 && Arguments[0].IsPageable;
+
+ ///
+ /// Validates if the current type is equal to .
+ ///
+ /// The type to compare.
+ /// Flag used to control if nullability should be ignored during comparison.
+ /// true if the types are equal, false otherwise.
+ protected bool Equals(CSharpType other, bool ignoreNullable = false)
+ => Equals(_implementation, other._implementation) &&
+ _type == other._type &&
+ Arguments.SequenceEqual(other.Arguments) &&
+ (ignoreNullable || IsNullable == other.IsNullable);
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public sealed override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj))
+ return false;
+ if (ReferenceEquals(this, obj))
+ return true;
+ return obj is CSharpType csType && Equals(csType, ignoreNullable: false);
+ }
+
+ public bool EqualsIgnoreNullable(CSharpType other) => Equals(other, ignoreNullable: true);
+
+ public bool Equals(Type type) =>
+ IsFrameworkType && (type.IsGenericType ? type.GetGenericTypeDefinition() == FrameworkType && AreArgumentsEqual(type.GetGenericArguments()) : type == FrameworkType);
+
+ ///
+ /// Types that the provided generic arguments match the type's arguments.
+ ///
+ /// The arguments to compare.
+ /// true if the arguments are equal to the type's arguments. Otherwise, false.
+ private bool AreArgumentsEqual(IReadOnlyList genericArguments)
+ {
+ if (Arguments.Count != genericArguments.Count)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < Arguments.Count; i++)
+ {
+ if (!Arguments[i].Equals(genericArguments[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public sealed override int GetHashCode()
+ {
+ // we cache the hashcode since `CSharpType` is meant to be immuttable.
+ if (_hashCode != null)
+ return _hashCode.Value;
+
+ var hashCode = new HashCode();
+ foreach (var arg in Arguments)
+ {
+ hashCode.Add(arg);
+ }
+ _hashCode = HashCode.Combine(_implementation, _type, hashCode.ToHashCode(), IsNullable);
+
+ return _hashCode.Value;
+ }
+
+ public CSharpType GetGenericTypeDefinition()
+ => _type is null
+ ? throw new NotSupportedException($"{nameof(TypeProvider)} doesn't support generics.")
+ : new(_type, IsNullable);
+
+ ///
+ /// Constructs a new with the given nullability .
+ ///
+ /// Flag to determine if the new type is nullable.
+ /// The existing if it is nullable, otherwise a new instance of .
+ public virtual CSharpType WithNullable(bool isNullable) =>
+ isNullable == IsNullable ? this : IsFrameworkType
+ ? new CSharpType(FrameworkType, Arguments, isNullable)
+ {
+ Literal = Literal,
+ UnionItemTypes = UnionItemTypes
+ }
+ : new CSharpType(Implementation, arguments: Arguments, isNullable: isNullable)
+ {
+ Literal = Literal,
+ UnionItemTypes = UnionItemTypes
+ };
+
+ public static implicit operator CSharpType(Type type) => new CSharpType(type);
+
+ public sealed override string ToString()
+ {
+ return new CodeWriter().Append($"{this}").ToString(false);
+ }
+
+ internal bool TryGetCSharpFriendlyName([MaybeNullWhen(false)] out string name)
+ {
+ name = _type switch
+ {
+ null => null,
+ var t when t.IsGenericParameter => t.Name,
+ //if we have an array type and the element is defined in the same assembly then its a generic param array.
+ var t when t.IsArray && t.Assembly.Equals(GetType().Assembly) => t.Name,
+ var t when t == typeof(bool) => "bool",
+ var t when t == typeof(byte) => "byte",
+ var t when t == typeof(sbyte) => "sbyte",
+ var t when t == typeof(short) => "short",
+ var t when t == typeof(ushort) => "ushort",
+ var t when t == typeof(int) => "int",
+ var t when t == typeof(uint) => "uint",
+ var t when t == typeof(long) => "long",
+ var t when t == typeof(ulong) => "ulong",
+ var t when t == typeof(char) => "char",
+ var t when t == typeof(double) => "double",
+ var t when t == typeof(float) => "float",
+ var t when t == typeof(object) => "object",
+ var t when t == typeof(decimal) => "decimal",
+ var t when t == typeof(string) => "string",
+ _ => null
+ };
+
+ return name != null;
+ }
+
+ ///
+ /// Method checks if object of "from" type can be converted to "to" type by calling `ToList` extension method.
+ /// It returns true if "from" is and "to" is or .
+ ///
+ internal static bool RequiresToList(CSharpType from, CSharpType to)
+ {
+ if (!to.IsFrameworkType || !from.IsFrameworkType || from.FrameworkType != typeof(IEnumerable<>))
+ {
+ return false;
+ }
+
+ return to.FrameworkType == typeof(IReadOnlyList<>) || to.FrameworkType == typeof(IList<>);
+ }
+
+ internal static CSharpType FromSystemType(Type type, string defaultNamespace, SourceInputModel? sourceInputModel, IEnumerable? backingProperties = null)
+ {
+ var systemObjectType = SystemObjectType.Create(type, defaultNamespace, sourceInputModel, backingProperties);
+ return systemObjectType.Type;
+ }
+
+ ///
+ /// This function is used to create a new CSharpType instance with a literal value.
+ /// If the type is a framework type, the CSharpType will be created with the literal value Constant
+ /// object.
+ ///
+ /// The original type to create a new CSharpType instance from.
+ /// The literal value of the type, if any.
+ /// An instance of CSharpType with a literal value property.
+ public static CSharpType FromLiteral(CSharpType type, object literalValue)
+ {
+ if (type.IsFrameworkType)
+ {
+ Constant? literal;
+ try
+ {
+ literal = new Constant(literalValue, type);
+ }
+ catch
+ {
+ literal = null;
+ }
+
+ return new CSharpType(type.FrameworkType, type.IsNullable)
+ {
+ Literal = literal
+ };
+ }
+
+ throw new NotSupportedException("Literals are not supported in non-framework type");
+ }
+
+ ///
+ /// Constructs a CSharpType that represents a union type.
+ ///
+ /// The list of union item types.
+ /// Flag used to determine if a type is nullable.
+ /// A instance representing those unioned types.
+ public static CSharpType FromUnion(IReadOnlyList unionItemTypes, bool isNullable = false)
+ {
+ return new CSharpType(typeof(BinaryData), isNullable)
+ {
+ UnionItemTypes = unionItemTypes
+ };
+ }
+
+ internal static CSharpType FromSystemType(BuildContext context, Type type, IEnumerable? backingProperties = null)
+ => FromSystemType(type, context.DefaultNamespace, context.SourceInputModel, backingProperties);
+
+ ///
+ /// Check whether two CSharpType instances equal or not
+ /// This is not the same as left.Equals(right) because this function only checks the names.
+ ///
+ /// The instance to compare to.
+ /// true if the instance are equal. false otherwise.
+ public bool AreNamesEqual(CSharpType? other)
+ {
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ if (other is null)
+ {
+ return false;
+ }
+
+ if (Namespace != other.Namespace)
+ return false;
+
+ if (Name != other.Name)
+ return false;
+
+ if (Arguments.Count != other.Arguments.Count)
+ return false;
+
+ for (int i = 0; i < Arguments.Count; i++)
+ {
+ if (!Arguments[i].AreNamesEqual(other.Arguments[i]))
+ return false;
+ }
+
+ return true;
+ }
+
+ public CSharpType MakeGenericType(IReadOnlyList arguments)
+ {
+ if (IsFrameworkType)
+ {
+ return new CSharpType(FrameworkType, arguments, IsNullable);
+ }
+ else
+ {
+ return new CSharpType(Implementation, arguments, IsNullable);
+ }
+ }
+
+ internal bool CanBeInitializedInline(Constant? defaultValue)
+ {
+ Debug.Assert(defaultValue.HasValue);
+
+ if (!Equals(defaultValue.Value.Type) && !defaultValue.Value.Type.CanBeInitializedInline(defaultValue))
+ {
+ return false;
+ }
+
+ if (Equals(typeof(string)) || Equals(typeof(Uri)))
+ {
+ return true;
+ }
+
+ if (this is { IsFrameworkType: false, Implementation: EnumType { IsExtensible: true } } && defaultValue.Value.Value != null)
+ {
+ return defaultValue.Value.IsNewInstanceSentinel;
+ }
+
+ return IsValueType || defaultValue.Value.Value == null;
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Types/TypeFactory.cs b/logger/autorest.csharp/common/Generation/Types/TypeFactory.cs
new file mode 100644
index 0000000..23323e8
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Types/TypeFactory.cs
@@ -0,0 +1,325 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using AutoRest.CSharp.Common.Input;
+using AutoRest.CSharp.Common.Input.InputTypes;
+using AutoRest.CSharp.Input;
+using AutoRest.CSharp.Output.Models.Types;
+using Azure;
+using Azure.Core;
+using Azure.Core.Expressions.DataFactory;
+using Azure.ResourceManager.Models;
+using Azure.ResourceManager.Resources.Models;
+using Microsoft.CodeAnalysis;
+
+namespace AutoRest.CSharp.Generation.Types
+{
+ internal class TypeFactory
+ {
+ private readonly OutputLibrary _library;
+ public Type UnknownType { get; }
+
+ public TypeFactory(OutputLibrary library, Type unknownType)
+ {
+ _library = library;
+ UnknownType = unknownType;
+ }
+
+ private static readonly Type _azureResponseErrorType = typeof(ResponseError);
+ private static readonly Type _managedIdentity = typeof(ManagedServiceIdentity);
+ private static readonly Type _userAssignedIdentity = typeof(UserAssignedIdentity);
+ private static readonly Type _extendedLocation = typeof(ExtendedLocation);
+
+ ///
+ /// This method will attempt to retrieve the of the input type.
+ ///
+ /// The input type to convert.
+ /// The of the input type.
+ public CSharpType CreateType(InputType inputType) => inputType switch
+ {
+ InputLiteralType literalType => CSharpType.FromLiteral(CreateType(literalType.ValueType), literalType.Value),
+ InputUnionType unionType => CSharpType.FromUnion(unionType.VariantTypes.Select(CreateType).ToArray()),
+ InputListType { CrossLanguageDefinitionId: "Azure.Core.EmbeddingVector" } listType => new CSharpType(typeof(ReadOnlyMemory<>), CreateType(listType.ValueType)),
+ InputListType listType => new CSharpType(typeof(IList<>), CreateType(listType.ValueType)),
+ InputDictionaryType dictionaryType => new CSharpType(typeof(IDictionary<,>), typeof(string), CreateType(dictionaryType.ValueType)),
+ InputEnumType enumType => _library.ResolveEnum(enumType),
+ InputModelType model => CreateModelType(model),
+ InputNullableType nullableType => CreateType(nullableType.Type).WithNullable(true),
+ InputPrimitiveType primitiveType => CreatePrimitiveType(primitiveType),
+ InputDateTimeType dateTimeType => new CSharpType(typeof(DateTimeOffset)),
+ InputDurationType durationType => new CSharpType(typeof(TimeSpan)),
+ _ => throw new InvalidOperationException($"Unknown type: {inputType}")
+ };
+
+ private CSharpType CreatePrimitiveType(in InputPrimitiveType inputType)
+ {
+ InputPrimitiveType? primitiveType = inputType;
+ while (primitiveType != null)
+ {
+ if (_knownAzurePrimitiveTypes.TryGetValue(primitiveType.CrossLanguageDefinitionId, out var knownType))
+ {
+ return knownType;
+ }
+
+ primitiveType = primitiveType.BaseType;
+ }
+
+ return inputType.Kind switch
+ {
+ InputPrimitiveTypeKind.Boolean => new CSharpType(typeof(bool)),
+ InputPrimitiveTypeKind.Bytes => Configuration.ShouldTreatBase64AsBinaryData ? new CSharpType(typeof(BinaryData)) : new CSharpType(typeof(byte[])),
+ InputPrimitiveTypeKind.PlainDate => new CSharpType(typeof(DateTimeOffset)),
+ InputPrimitiveTypeKind.Decimal => new CSharpType(typeof(decimal)),
+ InputPrimitiveTypeKind.Decimal128 => new CSharpType(typeof(decimal)),
+ InputPrimitiveTypeKind.PlainTime => new CSharpType(typeof(TimeSpan)),
+ InputPrimitiveTypeKind.Float32 => new CSharpType(typeof(float)),
+ InputPrimitiveTypeKind.Float64 => new CSharpType(typeof(double)),
+ InputPrimitiveTypeKind.Int8 => new CSharpType(typeof(sbyte)),
+ InputPrimitiveTypeKind.UInt8 => new CSharpType(typeof(byte)),
+ InputPrimitiveTypeKind.Int32 => new CSharpType(typeof(int)),
+ InputPrimitiveTypeKind.Int64 => new CSharpType(typeof(long)),
+ InputPrimitiveTypeKind.SafeInt => new CSharpType(typeof(long)),
+ InputPrimitiveTypeKind.Integer => new CSharpType(typeof(long)), // in typespec, integer is the base type of int related types, see type relation: https://typespec.io/docs/language-basics/type-relations
+ InputPrimitiveTypeKind.Float => new CSharpType(typeof(double)), // in typespec, float is the base type of float32 and float64, see type relation: https://typespec.io/docs/language-basics/type-relations
+ InputPrimitiveTypeKind.Numeric => new CSharpType(typeof(double)), // in typespec, numeric is the base type of number types, see type relation: https://typespec.io/docs/language-basics/type-relations
+ InputPrimitiveTypeKind.Stream => new CSharpType(typeof(Stream)),
+ InputPrimitiveTypeKind.String => new CSharpType(typeof(string)),
+ InputPrimitiveTypeKind.Url => new CSharpType(typeof(Uri)),
+ InputPrimitiveTypeKind.Unknown => UnknownType,
+ _ => new CSharpType(typeof(object)),
+ };
+ }
+
+ private static readonly IReadOnlyDictionary _knownAzurePrimitiveTypes = new Dictionary
+ {
+ [InputPrimitiveType.UuidId] = typeof(Guid),
+ [InputPrimitiveType.IPv4AddressId] = typeof(IPAddress),
+ [InputPrimitiveType.IPv6AddressId] = typeof(IPAddress),
+ [InputPrimitiveType.ETagId] = typeof(ETag),
+ [InputPrimitiveType.AzureLocationId] = typeof(AzureLocation),
+ [InputPrimitiveType.ArmIdId] = typeof(ResourceIdentifier),
+
+ [InputPrimitiveType.CharId] = typeof(char),
+ [InputPrimitiveType.ContentTypeId] = typeof(ContentType),
+ [InputPrimitiveType.ResourceTypeId] = typeof(ResourceType),
+
+ [InputPrimitiveType.ObjectId] = typeof(object),
+ [InputPrimitiveType.RequestMethodId] = typeof(RequestMethod),
+ [InputPrimitiveType.IPAddressId] = typeof(IPAddress),
+ };
+
+ private IReadOnlyDictionary? _knownAzureModelTypes;
+ private IReadOnlyDictionary KnownAzureModelTypes => _knownAzureModelTypes ??= new Dictionary
+ {
+ ["Azure.Core.Foundations.Error"] = SystemObjectType.Create(_azureResponseErrorType, _azureResponseErrorType.Namespace!, null).Type,
+ ["Azure.ResourceManager.CommonTypes.ManagedServiceIdentity"] = SystemObjectType.Create(_managedIdentity, _managedIdentity.Namespace!, null).Type,
+ ["Azure.ResourceManager.CommonTypes.UserAssignedIdentity"] = SystemObjectType.Create(_userAssignedIdentity, _userAssignedIdentity.Namespace!, null).Type,
+ ["Azure.ResourceManager.CommonTypes.ExtendedLocation"] = SystemObjectType.Create(_extendedLocation, _extendedLocation.Namespace!, null).Type,
+ };
+
+ private CSharpType CreateModelType(in InputModelType model)
+ {
+ // special handling data factory element
+ if (model.CrossLanguageDefinitionId == "Azure.Core.Resources.DataFactoryElement")
+ {
+ return new CSharpType(typeof(DataFactoryElement<>), CreateTypeForDataFactoryElement(model.ArgumentTypes![0]));
+ }
+ // handle other known model types
+ if (KnownAzureModelTypes.TryGetValue(model.CrossLanguageDefinitionId, out var type))
+ {
+ return type;
+ }
+ return _library.ResolveModel(model);
+ }
+
+ ///
+ /// This method is a shimming layer for HLC specially in DFE. In DFE, we always have `BinaryData` instead of `object` therefore we need to escape the `Any` to always return `BinaryData`.
+ ///
+ ///
+ ///
+ private CSharpType CreateTypeForDataFactoryElement(InputType inputType) => inputType switch
+ {
+ InputPrimitiveType { Kind: InputPrimitiveTypeKind.Unknown } => typeof(BinaryData),
+ _ => CreateType(inputType)
+ };
+
+ internal static Type? ToXMsFormatType(string? format) => format switch
+ {
+ XMsFormat.ArmId => typeof(ResourceIdentifier),
+ XMsFormat.AzureLocation => typeof(AzureLocation),
+ XMsFormat.DateTime => typeof(DateTimeOffset),
+ XMsFormat.DateTimeRFC1123 => typeof(DateTimeOffset),
+ XMsFormat.DateTimeUnix => typeof(DateTimeOffset),
+ XMsFormat.DurationConstant => typeof(TimeSpan),
+ XMsFormat.ETag => typeof(ETag),
+ XMsFormat.ResourceType => typeof(ResourceType),
+ XMsFormat.Object => typeof(object),
+ XMsFormat.IPAddress => typeof(IPAddress),
+ XMsFormat.ContentType => typeof(ContentType),
+ XMsFormat.RequestMethod => typeof(RequestMethod),
+ XMsFormat.DataFactoryElementOfString => typeof(DataFactoryElement),
+ XMsFormat.DataFactoryElementOfInt => typeof(DataFactoryElement),
+ XMsFormat.DataFactoryElementOfDouble => typeof(DataFactoryElement),
+ XMsFormat.DataFactoryElementOfBool => typeof(DataFactoryElement),
+ XMsFormat.DataFactoryElementOfDateTime => typeof(DataFactoryElement),
+ XMsFormat.DataFactoryElementOfDuration => typeof(DataFactoryElement),
+ XMsFormat.DataFactoryElementOfUri => typeof(DataFactoryElement),
+ XMsFormat.DataFactoryElementOfObject => typeof(DataFactoryElement),
+ XMsFormat.DataFactoryElementOfListOfString => typeof(DataFactoryElement>),
+ XMsFormat.DataFactoryElementOfKeyValuePairs => typeof(DataFactoryElement>),
+ XMsFormat.DataFactoryElementOfKeyObjectValuePairs => typeof(DataFactoryElement>),
+ _ => null
+ };
+
+ public CSharpType CreateType(ITypeSymbol symbol)
+ {
+ if (!TryCreateType(symbol, out var type))
+ {
+ throw new InvalidOperationException($"Unable to find a model or framework type that corresponds to {symbol}");
+ }
+
+ return type;
+ }
+
+ private static bool NoTypeValidator(System.Type type) => true;
+
+ public bool TryCreateType(ITypeSymbol symbol, [NotNullWhen(true)] out CSharpType? type)
+ => TryCreateType(symbol, NoTypeValidator, out type);
+
+ public CSharpType? GetLibraryTypeByName(string name) => _library.FindTypeByName(name);
+
+ public bool TryCreateType(ITypeSymbol symbol, Func validator, [NotNullWhen(true)] out CSharpType? type)
+ {
+ type = null;
+ return symbol switch
+ {
+ IArrayTypeSymbol arrayTypeSymbol => TryCreateTypeForIArrayTypeSymbol(arrayTypeSymbol, validator, out type),
+ INamedTypeSymbol namedTypeSymbol => TryCreateTypeForINamedTypeSymbol(namedTypeSymbol, validator, out type),
+
+ // We can only handle IArrayTypeSymbol of framework type and INamedTypeSymbol for now since CSharpType can't represent other types such as IArrayTypeSymbol of user types
+ // Instead of throwing an exception, which causes more side effects, we just return false and let the caller handle it.
+ _ => false
+ };
+ }
+
+ private bool TryCreateTypeForINamedTypeSymbol(INamedTypeSymbol namedTypeSymbol, Func validator, [NotNullWhen(true)] out CSharpType? type)
+ {
+ type = null;
+ if (namedTypeSymbol.ConstructedFrom.SpecialType == SpecialType.System_Nullable_T)
+ {
+ if (!TryCreateType(namedTypeSymbol.TypeArguments[0], validator, out type))
+ {
+ return false;
+ }
+ type = type.WithNullable(true);
+ return true;
+ }
+
+ Type? existingType = TryGetFrameworkType(namedTypeSymbol);
+
+ if (existingType is not null && validator(existingType))
+ {
+ if (!TryPopulateArguments(namedTypeSymbol.TypeArguments, validator, out var arguments))
+ {
+ return false;
+ }
+ type = new CSharpType(existingType, arguments, isNullable: false);
+ }
+ else
+ {
+ type = _library.FindTypeByName(namedTypeSymbol.Name);
+ }
+
+ if (type is null)
+ {
+ return false;
+ }
+
+ if (!type.IsValueType &&
+ namedTypeSymbol.NullableAnnotation != NullableAnnotation.NotAnnotated)
+ {
+ type = type.WithNullable(true);
+ }
+
+ return true;
+ }
+
+ private bool TryCreateTypeForIArrayTypeSymbol(IArrayTypeSymbol symbol, Func validator, [NotNullWhen(true)] out CSharpType? type)
+ {
+ type = null;
+ if (symbol is not IArrayTypeSymbol arrayTypeSymbol)
+ {
+ return false;
+ }
+
+ // For IArrayTypeSymbol, we can only handle it when the element type is a framework type.
+ var arrayType = TryGetFrameworkType(arrayTypeSymbol);
+ if (arrayType is not null && validator(arrayType))
+ {
+ type = new CSharpType(arrayType, arrayType.IsValueType && symbol.NullableAnnotation != NullableAnnotation.NotAnnotated);
+ return true;
+ }
+ return false;
+ }
+
+ private Type? TryGetFrameworkType(ISymbol namedTypeSymbol)
+ {
+ var fullMetadataName = GetFullMetadataName(namedTypeSymbol);
+ var fullyQualifiedName = $"{fullMetadataName}, {namedTypeSymbol.ContainingAssembly?.Name}";
+ return Type.GetType(fullMetadataName) ?? Type.GetType(fullyQualifiedName);
+ }
+
+ // There can be argument type missing
+ private bool TryPopulateArguments(ImmutableArray typeArguments, Func validator, [NotNullWhen(true)] out IReadOnlyList? arguments)
+ {
+ arguments = null;
+ var result = new List();
+ foreach (var typeArgument in typeArguments)
+ {
+ if (!TryCreateType(typeArgument, validator, out CSharpType? type))
+ {
+ return false;
+ }
+ result.Add(type);
+ }
+ arguments = result;
+ return true;
+ }
+
+ private string GetFullMetadataName(ISymbol namedTypeSymbol)
+ {
+ StringBuilder builder = new StringBuilder();
+
+ BuildFullMetadataName(builder, namedTypeSymbol);
+
+ return builder.ToString();
+ }
+
+ private void BuildFullMetadataName(StringBuilder builder, ISymbol symbol)
+ {
+ if (symbol is IArrayTypeSymbol arrayTypeSymbol)
+ {
+ BuildFullMetadataName(builder, arrayTypeSymbol.ElementType);
+ builder.Append("[]");
+ return;
+ }
+
+ if (symbol.ContainingNamespace != null &&
+ !symbol.ContainingNamespace.IsGlobalNamespace)
+ {
+ BuildFullMetadataName(builder, symbol.ContainingNamespace);
+ builder.Append(".");
+ }
+
+ builder.Append(symbol.MetadataName);
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Writers/AspDotNetExtensionWriter.cs b/logger/autorest.csharp/common/Generation/Writers/AspDotNetExtensionWriter.cs
new file mode 100644
index 0000000..d21ef81
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Writers/AspDotNetExtensionWriter.cs
@@ -0,0 +1,105 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using AutoRest.CSharp.Common.Output.Models.Types;
+using AutoRest.CSharp.Generation.Writers;
+using AutoRest.CSharp.Output.Models;
+using Azure.Core;
+using Azure.Core.Extensions;
+
+namespace AutoRest.CSharp.Common.Generation.Writers
+{
+ internal class AspDotNetExtensionWriter
+ {
+ private CodeWriter _writer;
+
+ private AspDotNetExtensionTypeProvider This { get; }
+
+ public AspDotNetExtensionWriter(AspDotNetExtensionTypeProvider aspDotNetExtension)
+ {
+ _writer = new CodeWriter();
+ This = aspDotNetExtension;
+ }
+
+ public void Write()
+ {
+ using (_writer.Namespace(This.Declaration.Namespace))
+ {
+ WriteClassDeclaration();
+ using (_writer.Scope())
+ {
+ WriteImplementations();
+ }
+ }
+ }
+
+ private void WriteClassDeclaration()
+ {
+ _writer.WriteXmlDocumentationSummary(This.Description);
+ _writer.AppendRaw(This.Declaration.Accessibility)
+ .AppendRaw(" static")
+ .Append($" partial class {This.Type.Name}");
+ }
+
+ private void WriteImplementations()
+ {
+ foreach (var (signature, (declarations, values)) in This.ExtesnsionMethods)
+ {
+ using (_writer.WriteCommonMethodWithoutValidation(signature, null, false, false))
+ {
+ var builder = signature.Parameters.First();
+ var arguments = signature.ReturnType!.Arguments;
+ var clientType = arguments.First();
+ _writer.Append($"return {builder.Name:I}.RegisterClientFactory")
+ .AppendRaw("<");
+ foreach (var argument in arguments)
+ {
+ _writer.Append($"{argument},");
+ }
+ _writer.RemoveTrailingComma();
+ _writer.AppendRaw(">((");
+ foreach (var declaration in declarations)
+ {
+ _writer.Append($"{declaration},");
+ }
+ _writer.RemoveTrailingComma();
+ _writer.Append($") => new {clientType}(");
+ foreach (var value in values)
+ {
+ _writer.Append($"{value},");
+ }
+ _writer.RemoveTrailingComma();
+ _writer.LineRaw("));");
+ }
+ _writer.Line();
+ }
+
+ foreach (var signature in This.ExtensionMethodsWithoutCallback)
+ {
+ using (_writer.WriteCommonMethodWithoutValidation(signature, null, false, false))
+ {
+ var builder = signature.Parameters.First();
+ var otherParameters = signature.Parameters.Skip(1);
+ _writer.Append($"return {builder.Name:I}.RegisterClientFactory")
+ .AppendRaw("<");
+ foreach (var argument in signature.ReturnType!.Arguments)
+ {
+ _writer.Append($"{argument},");
+ }
+ _writer.RemoveTrailingComma();
+ _writer.AppendRaw(">(");
+ foreach (var parameter in otherParameters)
+ {
+ _writer.Append($"{parameter.Name:I},");
+ }
+ _writer.RemoveTrailingComma();
+ _writer.LineRaw(");");
+ }
+ }
+ }
+
+ public override string ToString() => _writer.ToString();
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Writers/CSProjWriter.cs b/logger/autorest.csharp/common/Generation/Writers/CSProjWriter.cs
new file mode 100644
index 0000000..d371106
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Writers/CSProjWriter.cs
@@ -0,0 +1,223 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+using System.Xml;
+
+namespace AutoRest.CSharp.Generation.Writers;
+
+internal class CSProjWriter
+{
+ public CSProjWriter()
+ {
+ ProjectReferences = new List();
+ PackageReferences = new List();
+ PrivatePackageReferences = new List();
+ CompileIncludes = new List();
+ }
+
+ public CSProjProperty? Description { get; init; }
+
+ public CSProjProperty? AssemblyTitle { get; init; }
+
+ public CSProjProperty? Version { get; init; }
+
+ public CSProjProperty? PackageTags { get; init; }
+
+ public CSProjProperty? TargetFrameworks { get; init; }
+
+ public CSProjProperty? TargetFramework { get; init; }
+
+ public CSProjProperty? IncludeOperationsSharedSource { get; init; }
+
+ public CSProjProperty? LangVersion { get; init; }
+
+ public CSProjProperty? GenerateDocumentationFile { get; init; }
+
+ public CSProjProperty? NoWarn { get; init; }
+
+ public CSProjProperty? TreatWarningsAsErrors { get; init; }
+
+ public CSProjProperty? Nullable { get; init; }
+
+ public CSProjProperty? IncludeManagementSharedCode { get; init; }
+
+ public CSProjProperty? IncludeGeneratorSharedCode { get; init; }
+
+ public CSProjProperty? DefineConstants { get; init; }
+
+ public CSProjProperty? RestoreAdditionalProjectSources { get; init; }
+
+ public IList ProjectReferences { get; }
+
+ public IList PackageReferences { get; }
+
+ public IList PrivatePackageReferences { get; }
+
+ public IList CompileIncludes { get; }
+
+ public string Write()
+ {
+ var builder = new StringBuilder();
+ using var writer = XmlWriter.Create(builder, new XmlWriterSettings
+ {
+ OmitXmlDeclaration = true,
+ Indent = true
+ });
+ writer.WriteStartDocument();
+ // write the Project element
+ writer.WriteStartElement("Project");
+ writer.WriteAttributeString("Sdk", "Microsoft.NET.Sdk");
+ // write properties
+ WriteProperties(writer);
+
+ // write the first ItemGroup for compile include
+ if (CompileIncludes.Count > 0)
+ {
+ // this is the only way I know to write a blank line in an xml document using APIs instead of just write raw strings
+ // feel free to change this if other elegant way is found.
+ writer.Flush();
+ builder.AppendLine();
+ writer.WriteStartElement("ItemGroup");
+ foreach (var compileInclude in CompileIncludes)
+ {
+ WriteCompileInclude(writer, compileInclude);
+ }
+ writer.WriteEndElement();
+ }
+
+ // write project references
+ if (ProjectReferences.Count > 0)
+ {
+ writer.Flush();
+ builder.AppendLine();
+ writer.WriteStartElement("ItemGroup");
+ foreach (var package in ProjectReferences)
+ {
+ WriteProjectReference(writer, package);
+ }
+ writer.WriteEndElement();
+ }
+
+ // write package references
+ if (PackageReferences.Count > 0)
+ {
+ writer.Flush();
+ builder.AppendLine();
+ writer.WriteStartElement("ItemGroup");
+ foreach (var package in PackageReferences)
+ {
+ WritePackageReference(writer, package);
+ }
+ writer.WriteEndElement();
+ }
+
+ // write private package references
+ if (PrivatePackageReferences.Count > 0)
+ {
+ writer.Flush();
+ builder.AppendLine();
+ writer.WriteStartElement("ItemGroup");
+ foreach (var package in PrivatePackageReferences)
+ {
+ WritePackageReference(writer, package, true);
+ }
+ writer.WriteEndElement();
+ }
+
+ writer.WriteEndDocument();
+ writer.Close();
+ writer.Flush();
+
+ // add an empty on the end of file
+ builder.AppendLine();
+
+ return builder.ToString();
+ }
+
+ private static readonly IEnumerable _properties = typeof(CSProjWriter).GetProperties(BindingFlags.Public | BindingFlags.Instance);
+
+ private void WriteProperties(XmlWriter writer)
+ {
+ writer.WriteStartElement("PropertyGroup");
+ // this will write those properties in the same order as they are defined in this class
+ // introduce this method to save the effort of writing every property one by one
+ foreach (var property in _properties)
+ {
+ // only include those CSProjProperty types
+ if (property.PropertyType != typeof(CSProjProperty))
+ continue;
+ // invoke the WriteElementIfNotNull method on each of them
+ var value = (CSProjProperty?)property.GetValue(this);
+ WriteElementIfNotNull(writer, property.Name, value);
+ }
+ writer.WriteEndElement();
+ }
+
+ private void WriteElementIfNotNull(XmlWriter writer, string name, CSProjProperty? property)
+ {
+ if (property == null)
+ return;
+
+ if (property.Comment != null)
+ {
+ writer.WriteComment(property.Comment);
+ }
+
+ writer.WriteElementString(name, property.Value);
+ }
+
+ private void WriteCompileInclude(XmlWriter writer, CSProjCompileInclude compileInclude)
+ {
+ writer.WriteStartElement("Compile");
+ writer.WriteAttributeString("Include", compileInclude.Include);
+ if (compileInclude.LinkBase != null)
+ {
+ writer.WriteAttributeString("LinkBase", compileInclude.LinkBase);
+ }
+ writer.WriteEndElement();
+ }
+
+ private void WriteProjectReference(XmlWriter writer, CSProjDependencyPackage package)
+ {
+ writer.WriteStartElement("ProjectReference");
+ writer.WriteAttributeString("Include", package.PackageName);
+ writer.WriteEndElement();
+ }
+
+ private void WritePackageReference(XmlWriter writer, CSProjDependencyPackage package, bool isPrivateAsset = false)
+ {
+ writer.WriteStartElement("PackageReference");
+ writer.WriteAttributeString("Include", package.PackageName);
+ if (package.Version != null)
+ {
+ writer.WriteAttributeString("Version", package.Version);
+ }
+ if (isPrivateAsset)
+ {
+ writer.WriteAttributeString("PrivateAssets", "All");
+ }
+ writer.WriteEndElement();
+ }
+
+ public record CSProjProperty(string Value, string? Comment)
+ {
+ public CSProjProperty(string value) : this(value, null)
+ { }
+
+ public static implicit operator CSProjProperty(string value) => new(value, null);
+ public static implicit operator CSProjProperty(bool value) => new(XmlConvert.ToString(value), null);
+ }
+
+ public record CSProjDependencyPackage(string PackageName, string? Version)
+ {
+ public CSProjDependencyPackage(string packageName) : this(packageName, null) { }
+ }
+
+ public record CSProjCompileInclude(string Include, string? LinkBase)
+ {
+ public CSProjCompileInclude(string include) : this(include, null) { }
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Writers/ClientOptionsWriter.cs b/logger/autorest.csharp/common/Generation/Writers/ClientOptionsWriter.cs
new file mode 100644
index 0000000..577da73
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Writers/ClientOptionsWriter.cs
@@ -0,0 +1,67 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using AutoRest.CSharp.Common.Input;
+using AutoRest.CSharp.Output.Models.Types;
+using AutoRest.CSharp.Utilities;
+
+namespace AutoRest.CSharp.Generation.Writers
+{
+ internal class ClientOptionsWriter
+ {
+ public static void WriteClientOptions(CodeWriter writer, ClientOptionsTypeProvider clientOptions)
+ {
+ using (writer.Namespace(clientOptions.Type.Namespace))
+ {
+ writer.WriteXmlDocumentationSummary(clientOptions.Description);
+ using (writer.Scope($"{clientOptions.Declaration.Accessibility} partial class {clientOptions.Type.Name}: {Configuration.ApiTypes.ClientOptionsType}"))
+ {
+ if (clientOptions.ApiVersions?.Count > 0)
+ {
+ writer.Line($"private const ServiceVersion LatestVersion = ServiceVersion.{clientOptions.ApiVersions.Last().Name};");
+ writer.Line();
+ writer.WriteXmlDocumentationSummary($"The version of the service to use.");
+ using (writer.Scope($"public enum ServiceVersion"))
+ {
+ foreach (var apiVersion in clientOptions.ApiVersions)
+ {
+ writer.WriteXmlDocumentationSummary($"{apiVersion.Description}");
+ writer.Line($"{apiVersion.Name} = {apiVersion.Value:L},");
+ }
+ }
+ writer.Line();
+
+ writer.Line($"internal string Version {{ get; }}");
+ writer.Line();
+
+ writer.WriteXmlDocumentationSummary($"Initializes new instance of {clientOptions.Type.Name}.");
+ using (writer.Scope($"public {clientOptions.Type.Name}(ServiceVersion version = LatestVersion)"))
+ {
+ writer.Append($"Version = version ");
+ using (writer.Scope($"switch", end: "};"))
+ {
+ foreach (var apiVersion in clientOptions.ApiVersions)
+ {
+ writer.Line($"ServiceVersion.{apiVersion.Name} => {apiVersion.StringValue:L},");
+ }
+
+ writer.Line($"_ => throw new {typeof(NotSupportedException)}()");
+ }
+ }
+
+ writer.Line();
+ }
+
+ foreach (var parameter in clientOptions.AdditionalParameters)
+ {
+ writer.WriteXmlDocumentationSummary(parameter.Description);
+ writer.Line($"public {parameter.Type} {parameter.Name.ToCleanName()} {{ get; set; }}");
+ writer.Line();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Writers/ClientWriter.cs b/logger/autorest.csharp/common/Generation/Writers/ClientWriter.cs
new file mode 100644
index 0000000..8022931
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Writers/ClientWriter.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using AutoRest.CSharp.Common.Input;
+using AutoRest.CSharp.Generation.Writers;
+using AutoRest.CSharp.Output.Models;
+using AutoRest.CSharp.Output.Models.Requests;
+using Azure.Core.Pipeline;
+
+namespace AutoRest.CSharp.Common.Generation.Writers
+{
+ internal class ClientWriter
+ {
+ protected const string ClientDiagnosticsVariable = "clientDiagnostics";
+ protected const string PipelineVariable = "pipeline";
+ protected const string PipelineProperty = "Pipeline";
+ protected const string PipelineField = "_" + PipelineVariable;
+ protected const string DiagnosticsProperty = "Diagnostics";
+
+ protected static readonly Reference ClientDiagnosticsField = new Reference("_" + ClientDiagnosticsVariable, typeof(ClientDiagnostics));
+
+ protected virtual string RestClientAccessibility => "internal";
+
+ protected virtual string RestClientField => "RestClient";
+
+ protected static string CreateMethodName(string name, bool async) => async ? $"{name}Async" : name;
+
+ protected void WriteClientFields(CodeWriter writer, RestClient client, bool writePipelineField)
+ {
+ if (Configuration.IsBranded)
+ {
+ writer.Line($"private readonly {typeof(ClientDiagnostics)} {ClientDiagnosticsField.GetReferenceFormattable()};");
+ }
+ if (writePipelineField)
+ {
+ writer.Line($"private readonly {Configuration.ApiTypes.HttpPipelineType} {PipelineField};");
+ }
+ writer.Append($"{RestClientAccessibility} {client.Type} {RestClientField}").LineRaw(" { get; }");
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Writers/CodeWriter.cs b/logger/autorest.csharp/common/Generation/Writers/CodeWriter.cs
new file mode 100644
index 0000000..8b8e350
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Writers/CodeWriter.cs
@@ -0,0 +1,850 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using AutoRest.CSharp.Common.Input;
+using AutoRest.CSharp.Common.Output.Expressions.ValueExpressions;
+using AutoRest.CSharp.Generation.Types;
+using AutoRest.CSharp.Output.Models;
+using AutoRest.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace AutoRest.CSharp.Generation.Writers
+{
+ internal class CodeWriter
+ {
+ private const int DefaultLength = 1024;
+ private static readonly string _newLine = "\n";
+ private static readonly string _braceNewLine = "{\n";
+
+ private readonly HashSet _usingNamespaces = new HashSet();
+
+ private readonly Stack _scopes;
+ private string? _currentNamespace;
+
+ private char[] _builder;
+ private int _length;
+ private bool _writingXmlDocumentation;
+
+ public CodeWriter()
+ {
+ _builder = ArrayPool.Shared.Rent(DefaultLength);
+
+ _scopes = new Stack();
+ _scopes.Push(new CodeWriterScope(this, "", false));
+ }
+
+ public CodeWriterScope Scope(FormattableString line, string start = "{", string end = "}", bool newLine = true, CodeWriterScopeDeclarations? scopeDeclarations = null)
+ {
+ ValidateDeclarations(scopeDeclarations);
+ CodeWriterScope codeWriterScope = new CodeWriterScope(this, end, newLine);
+ _scopes.Push(codeWriterScope);
+ Line(line);
+ LineRaw(start);
+ AddDeclarationsToScope(scopeDeclarations);
+ return codeWriterScope;
+ }
+
+ public CodeWriterScope Scope()
+ {
+ return ScopeRaw();
+ }
+
+ private void ValidateDeclarations(CodeWriterScopeDeclarations? scopeDeclarations)
+ {
+ if (scopeDeclarations == null)
+ {
+ return;
+ }
+
+ foreach (var declarationName in scopeDeclarations.Names)
+ {
+ if (!IsAvailable(declarationName))
+ {
+ throw new InvalidOperationException($"Variable with name '{declarationName}' is declared already.");
+ }
+ }
+ }
+
+ private void AddDeclarationsToScope(CodeWriterScopeDeclarations? scopeDeclarations)
+ {
+ if (scopeDeclarations == null)
+ {
+ return;
+ }
+
+ var currentScope = _scopes.Peek();
+
+ foreach (var declarationName in scopeDeclarations.Names)
+ {
+ foreach (var scope in _scopes)
+ {
+ scope.AllDefinedIdentifiers.Add(declarationName);
+ }
+
+ currentScope.Identifiers.Add(declarationName);
+ }
+ }
+
+ private CodeWriterScope ScopeRaw(string start = "{", string end = "}", bool newLine = true)
+ {
+ LineRaw(start);
+ CodeWriterScope codeWriterScope = new CodeWriterScope(this, end, newLine);
+ _scopes.Push(codeWriterScope);
+ return codeWriterScope;
+ }
+
+ public CodeWriterScope Namespace(string @namespace)
+ {
+ _currentNamespace = @namespace;
+ Line($"namespace {@namespace}");
+ return Scope();
+ }
+
+ public CodeWriter Append(FormattableString formattableString)
+ {
+ if (formattableString.ArgumentCount == 0)
+ {
+ return AppendRaw(formattableString.ToString());
+ }
+
+ const string literalFormatString = ":L";
+ const string declarationFormatString = ":D"; // :D :)
+ const string identifierFormatString = ":I";
+ const string crefFormatString = ":C"; // wraps content into "see cref" tag, available only in xmlDoc
+ foreach ((var span, bool isLiteral, int index) in StringExtensions.GetPathParts(formattableString.Format))
+ {
+ if (isLiteral)
+ {
+ AppendRaw(span);
+ continue;
+ }
+
+ var argument = formattableString.GetArgument(index);
+ var isDeclaration = span.EndsWith(declarationFormatString);
+ var isIdentifier = span.EndsWith(identifierFormatString);
+ var isLiteralFormat = span.EndsWith(literalFormatString);
+ var isCref = span.EndsWith(crefFormatString);
+
+ if (isCref)
+ {
+ if (!_writingXmlDocumentation)
+ {
+ throw new InvalidOperationException($"':C' formatter can be used only inside XmlDoc");
+ }
+
+ switch (argument)
+ {
+ case Type t:
+ AppendTypeForCRef(new CSharpType(t));
+ break;
+ case CSharpType t:
+ AppendTypeForCRef(t);
+ break;
+ default:
+ Append($"");
+ break;
+ }
+
+ continue;
+ }
+
+ switch (argument)
+ {
+ case IEnumerable fss:
+ foreach (var fs in fss)
+ {
+ Append(fs);
+ }
+ break;
+ case FormattableString fs:
+ Append(fs);
+ break;
+ case Type t:
+ AppendType(new CSharpType(t), false, false);
+ break;
+ case CSharpType t:
+ AppendType(t, isDeclaration, false);
+ break;
+ case CodeWriterDeclaration declaration when isDeclaration:
+ Declaration(declaration);
+ break;
+ case CodeWriterDeclaration declaration:
+ Append(declaration);
+ break;
+ case ValueExpression expression:
+ expression.Write(this);
+ break;
+ case var _ when isLiteralFormat:
+ Literal(argument);
+ break;
+ default:
+ string? s = argument?.ToString();
+ if (s == null)
+ {
+ throw new ArgumentNullException(index.ToString());
+ }
+
+ if (isDeclaration)
+ {
+ Declaration(s);
+ }
+ else if (isIdentifier)
+ {
+ Identifier(s);
+ }
+ else
+ {
+ AppendRaw(s);
+ }
+ break;
+ }
+ }
+
+ return this;
+ }
+
+ public void UseNamespace(string @namespace)
+ {
+ if (_currentNamespace == @namespace)
+ {
+ return;
+ }
+
+ if (_currentNamespace is not null && _currentNamespace.Length > @namespace.Length && _currentNamespace.StartsWith(@namespace) && _currentNamespace[@namespace.Length] == '.')
+ {
+ return;
+ }
+
+ _usingNamespaces.Add(@namespace);
+ }
+
+ public CodeWriter WriteRawXmlDocumentation(FormattableString? content)
+ {
+ // if xml docs is globally turned off by the configuration, write nothing
+ if (Configuration.DisableXmlDocs)
+ return this;
+
+ if (content is null)
+ return this;
+
+ var lines = content.ToString().Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
+ var xmlLines = string.Join('\n', lines.Select(l => "/// " + l));
+ AppendRaw(xmlLines);
+ Line();
+ return this;
+ }
+
+ public CodeWriter AppendXmlDocumentation(FormattableString startTag, FormattableString endTag, FormattableString content)
+ {
+ // if xml docs is globally turned off by the configuration, write nothing
+ if (Configuration.DisableXmlDocs)
+ return this;
+
+ const string xmlDoc = "/// ";
+ const string xmlDocNewLine = "\n/// ";
+
+ var commentStart = _length;
+ AppendRaw(CurrentLine.IsEmpty ? xmlDoc : xmlDocNewLine);
+
+ var startTagStart = _length;
+ Append(startTag);
+ _writingXmlDocumentation = true;
+
+ var contentStart = _length;
+ if (content.Format.Length > 0)
+ {
+ Append(content);
+ }
+ var contentEnd = _length;
+
+ _writingXmlDocumentation = false;
+ Append(endTag);
+
+ if (contentStart == contentEnd)
+ {
+ var startTagSpan = WrittenText.Slice(startTagStart + 1, contentStart - startTagStart - 1);
+ var endTagSpan = WrittenText.Slice(contentEnd + 2);
+
+ if (startTagSpan.SequenceEqual(endTagSpan))
+ {
+ // Remove empty tags
+ _length = commentStart;
+ }
+ else
+ {
+ Line();
+ }
+
+ return this;
+ }
+
+ Line();
+ var contentSpan = _builder.AsSpan(contentStart, contentEnd - contentStart);
+
+ var lastLineBreak = contentSpan.LastIndexOf(_newLine);
+ if (lastLineBreak == -1)
+ {
+ // Add spaces and dot to match existing formatting
+ if (contentEnd > contentStart)
+ {
+ if (contentSpan[^1] != ' ')
+ {
+ InsertRaw(contentSpan[^1] == '.' ? " " : ". ", contentEnd);
+ }
+ else
+ {
+ var trimmedContentSpan = contentSpan.TrimEnd();
+ if (trimmedContentSpan[^1] != '.')
+ {
+ InsertRaw(".", contentStart + trimmedContentSpan.Length);
+ }
+ }
+
+ if (contentSpan[0] != ' ')
+ {
+ InsertRaw(" ", contentStart);
+ }
+ }
+ return this;
+ }
+
+ if (lastLineBreak != contentSpan.Length)
+ {
+ InsertRaw(xmlDocNewLine, contentEnd);
+ }
+
+ while (lastLineBreak != -1)
+ {
+ InsertRaw(xmlDoc, lastLineBreak + contentStart + 1);
+ contentSpan = contentSpan.Slice(0, lastLineBreak);
+ lastLineBreak = contentSpan.LastIndexOf(_newLine);
+ }
+
+ if (contentSpan.Length > 0)
+ {
+ InsertRaw(xmlDocNewLine, contentStart);
+ }
+
+ return this;
+ }
+
+ public CodeWriter WriteXmlDocumentationInheritDoc(CSharpType? crefType = null)
+ {
+ // TODO Temporary -- maybe we should consolidate this into the CodeWriter.AppendXmlDocumentation method as well
+ // currently the CodeWriter.AppendXmlDocumentation will remove empty tags, and it does not support we just write a tag like , therefore now we cannot use it here.
+ if (Configuration.DisableXmlDocs)
+ return this;
+
+ return crefType == null
+ ? Line($"/// ")
+ : Line($"/// ");
+ }
+
+ protected string GetTemporaryVariable(string s)
+ {
+ if (IsAvailable(s))
+ {
+ return s;
+ }
+
+ for (int i = 0; i < 100; i++)
+ {
+ var name = s + i;
+ if (IsAvailable(name))
+ {
+ return name;
+ }
+ }
+ throw new InvalidOperationException("Can't find suitable variable name.");
+ }
+
+ private bool IsAvailable(string s)
+ {
+ if (_scopes.TryPeek(out var currentScope))
+ {
+ if (currentScope.AllDefinedIdentifiers.Contains(s))
+ {
+ return false;
+ }
+ }
+
+ foreach (CodeWriterScope codeWriterScope in _scopes)
+ {
+ if (codeWriterScope.Identifiers.Contains(s))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void AppendTypeForCRef(CSharpType type)
+ {
+ // Because of the limitations of type cref in XmlDoc
+ // we add "?" nullability operator after `cref` block
+ var isNullable = type is { IsNullable: true, IsValueType: true };
+ var arguments = type.IsGenericType ? type.Arguments : null;
+
+ type = type.WithNullable(false);
+ if (type.IsGenericType)
+ {
+ type = type.GetGenericTypeDefinition();
+ }
+
+ AppendRaw($"");
+
+ if (isNullable)
+ {
+ AppendRaw("?");
+ }
+
+ if (arguments is not null)
+ {
+ for (int i = 0; i < arguments.Count; i++)
+ {
+ var argument = arguments[i];
+ if (argument is { IsFrameworkType: true, FrameworkType.IsGenericParameter: true })
+ {
+ continue;
+ }
+
+ AppendRaw(" where ");
+ AppendType(type.Arguments[i], false, false);
+ AppendRaw(" is");
+ if (argument.IsArray)
+ {
+ AppendRaw(" an array of type ");
+ argument = argument.ElementType;
+ }
+ else
+ {
+ AppendRaw(" of type ");
+ }
+
+ // If argument type is non-generic, we can provide "see cref" for it
+ // Otherwise, just write its name
+ if (argument.IsGenericType)
+ {
+ AppendRaw("");
+ AppendType(argument, false, true);
+ AppendRaw("");
+ }
+ else
+ {
+ AppendTypeForCRef(argument);
+ }
+
+ AppendRaw(",");
+ }
+ RemoveTrailingComma();
+ }
+ }
+
+ private void AppendType(CSharpType type, bool isDeclaration, bool writeTypeNameOnly)
+ {
+ if (type.IsFrameworkType && type.FrameworkType.IsArray && type.FrameworkType.GetGenericArguments().Any())
+ {
+ AppendType(type.FrameworkType.GetElementType()!, isDeclaration, writeTypeNameOnly);
+ AppendRaw("[]");
+ return;
+ }
+
+ if (type.TryGetCSharpFriendlyName(out var keywordName))
+ {
+ AppendRaw(keywordName);
+ if (type.FrameworkType.IsGenericParameter && type.IsNullable)
+ {
+ AppendRaw("?");
+ }
+ }
+ else if (isDeclaration && !type.IsFrameworkType)
+ {
+ AppendRaw(type.Implementation.Declaration.Name);
+ }
+ else if (writeTypeNameOnly)
+ {
+ AppendRaw(type.Name);
+ }
+ else if (type.DeclaringType != null)
+ {
+ AppendType(type.DeclaringType, isDeclaration, writeTypeNameOnly);
+ AppendRaw(".");
+ AppendRaw(type.Name);
+ }
+ else
+ {
+ UseNamespace(type.Namespace);
+
+ AppendRaw("global::");
+ AppendRaw(type.Namespace);
+ AppendRaw(".");
+ AppendRaw(type.Name);
+ }
+
+ if (type.Arguments.Any())
+ {
+ AppendRaw(_writingXmlDocumentation ? "{" : "<");
+ foreach (var typeArgument in type.Arguments)
+ {
+ AppendType(typeArgument, false, writeTypeNameOnly);
+ AppendRaw(_writingXmlDocumentation ? "," : ", ");
+ }
+ RemoveTrailingComma();
+ AppendRaw(_writingXmlDocumentation ? "}" : ">");
+ }
+
+ if (!isDeclaration && type is { IsNullable: true, IsValueType: true })
+ {
+ AppendRaw("?");
+ }
+ }
+
+ public CodeWriter Literal(object? o)
+ {
+ return AppendRaw(o switch
+ {
+ null => "null",
+ string s => SyntaxFactory.Literal(s).ToString(),
+ int i => SyntaxFactory.Literal(i).ToString(),
+ long l => SyntaxFactory.Literal(l).ToString(),
+ decimal d => SyntaxFactory.Literal(d).ToString(),
+ double d => SyntaxFactory.Literal(d).ToString(),
+ float f => SyntaxFactory.Literal(f).ToString(),
+ char c => SyntaxFactory.Literal(c).ToString(),
+ sbyte sc => SyntaxFactory.Literal(sc).ToString(),
+ byte b => SyntaxFactory.Literal(b).ToString(),
+ bool b => b ? "true" : "false",
+ BinaryData bd => bd.ToArray().Length == 0 ? "new byte[] { }" : SyntaxFactory.Literal(bd.ToString()).ToString(),
+ _ => throw new NotImplementedException($"Unknown literal type {o?.GetType().Name ?? "'null'"}")
+ });
+ }
+
+ public CodeWriter Line(FormattableString formattableString)
+ {
+ Append(formattableString);
+ Line();
+
+ return this;
+ }
+
+ public CodeWriter Line()
+ {
+ LineRaw(string.Empty);
+
+ return this;
+ }
+
+ public ReadOnlySpan WrittenText => _builder.AsSpan(0, _length);
+
+ private ReadOnlySpan PreviousLine
+ {
+ get
+ {
+ var writtenText = WrittenText;
+
+ var indexOfNewLine = writtenText.LastIndexOf(_newLine);
+ if (indexOfNewLine == -1)
+ {
+ return Span.Empty;
+ }
+
+ var writtenTextBeforeLastLine = writtenText.Slice(0, indexOfNewLine);
+ var indexOfPreviousNewLine = writtenTextBeforeLastLine.LastIndexOf(_newLine);
+ if (indexOfPreviousNewLine == -1)
+ {
+ return writtenText.Slice(0, indexOfNewLine + 1);
+ }
+
+ return writtenText.Slice(indexOfPreviousNewLine + 1, indexOfNewLine - indexOfPreviousNewLine);
+ }
+ }
+
+ private ReadOnlySpan CurrentLine
+ {
+ get
+ {
+ var writtenText = WrittenText;
+
+ var indexOfNewLine = writtenText.LastIndexOf(_newLine);
+ if (indexOfNewLine == -1)
+ {
+ return writtenText;
+ }
+
+ return writtenText.Slice(indexOfNewLine + 1);
+ }
+ }
+
+ private void EnsureSpace(int space)
+ {
+ if (_builder.Length - _length < space)
+ {
+ var newBuilder = ArrayPool.Shared.Rent(Math.Max(_builder.Length + space, _builder.Length * 2));
+ _builder.AsSpan().CopyTo(newBuilder);
+
+ ArrayPool.Shared.Return(_builder);
+ _builder = newBuilder;
+ }
+ }
+
+ public CodeWriter LineRaw(string str)
+ {
+ AppendRaw(str);
+
+ var previousLine = PreviousLine;
+
+ if (CurrentLine.IsEmpty &&
+ (previousLine.SequenceEqual(_newLine) || previousLine.EndsWith(_braceNewLine)))
+ {
+ return this;
+ }
+
+ AppendRaw(_newLine);
+
+ return this;
+ }
+
+ public CodeWriter AppendRaw(string str) => AppendRaw(str.AsSpan());
+
+ private CodeWriter AppendRaw(ReadOnlySpan span) => InsertRaw(span, _length);
+
+ private CodeWriter InsertRaw(ReadOnlySpan span, int position, bool skipNewLineCheck = false)
+ {
+ Debug.Assert(0 <= position);
+ Debug.Assert(position <= _length);
+
+ if (!skipNewLineCheck)
+ {
+ var newLineSpan = "\r\n".AsSpan();
+ var newLineIndex = span.IndexOf(newLineSpan);
+ while (newLineIndex != -1)
+ {
+ InsertRaw(span.Slice(0, newLineIndex), position, skipNewLineCheck: true);
+ position += newLineIndex;
+ span = span.Slice(newLineIndex + 1);
+ newLineIndex = span.IndexOf(newLineSpan);
+ }
+ }
+
+ EnsureSpace(span.Length);
+ if (position < _length)
+ {
+ Array.Copy(_builder, position, _builder, span.Length + position, _length - position);
+ }
+
+ span.CopyTo(_builder.AsSpan(position));
+ _length += span.Length;
+ return this;
+ }
+
+ public CodeWriter Identifier(string identifier)
+ {
+ if (StringExtensions.IsCSharpKeyword(identifier))
+ {
+ AppendRaw("@");
+ }
+ return AppendRaw(identifier);
+ }
+
+ protected CodeWriter Declaration(string declaration)
+ {
+ foreach (var scope in _scopes)
+ {
+ scope.AllDefinedIdentifiers.Add(declaration);
+ }
+
+ _scopes.Peek().Identifiers.Add(declaration);
+
+ return Identifier(declaration);
+ }
+
+ public virtual CodeWriter Declaration(CodeWriterDeclaration declaration)
+ {
+ if (_writingXmlDocumentation)
+ {
+ throw new InvalidOperationException("Can't declare variables inside documentation.");
+ }
+
+ declaration.SetActualName(GetTemporaryVariable(declaration.RequestedName));
+ _scopes.Peek().Declarations.Add(declaration);
+ return Declaration(declaration.ActualName);
+ }
+
+ public override string ToString()
+ {
+ return ToString(true);
+ }
+
+ public string ToString(bool header)
+ {
+ if (_length == 0)
+ {
+ return string.Empty;
+ }
+
+ var builder = new StringBuilder(_length);
+ IEnumerable namespaces = _usingNamespaces
+ .OrderByDescending(ns => ns.StartsWith("System"))
+ .ThenBy(ns => ns, StringComparer.Ordinal);
+ if (header)
+ {
+ builder.Append(Configuration.ApiTypes.LicenseString);
+ builder.AppendLine("// ");
+ builder.AppendLine();
+ builder.AppendLine("#nullable disable");
+ builder.AppendLine();
+
+ foreach (string ns in namespaces)
+ {
+ builder.Append("using ").Append(ns).AppendLine(";");
+ }
+
+ if (namespaces.Any())
+ {
+ builder.AppendLine();
+ }
+ }
+
+ // Normalize newlines
+ var spanLines = _builder.AsSpan(0, _length).EnumerateLines();
+ int lineCount = 0;
+ foreach (var line in spanLines)
+ {
+ builder.Append(line.TrimEnd());
+ builder.AppendLine();
+ lineCount++;
+ }
+ // Remove last new line if there are more than 1
+ if (lineCount > 1)
+ {
+ builder.Remove(builder.Length - Environment.NewLine.Length, Environment.NewLine.Length);
+ }
+ return builder.ToString();
+ }
+
+ internal class CodeWriterScope : IDisposable
+ {
+ private readonly CodeWriter _writer;
+ private readonly string? _end;
+ private readonly bool _newLine;
+
+ public HashSet Identifiers { get; } = new();
+
+ public HashSet AllDefinedIdentifiers { get; } = new();
+
+ public List Declarations { get; } = new();
+
+ public CodeWriterScope(CodeWriter writer, string? end, bool newLine)
+ {
+ _writer = writer;
+ _end = end;
+ _newLine = newLine;
+ }
+
+ public void Dispose()
+ {
+ if (_writer != null)
+ {
+ _writer.PopScope(this);
+ foreach (var declaration in Declarations)
+ {
+ declaration.SetActualName(null);
+ }
+
+ Declarations.Clear();
+
+ if (_end != null)
+ {
+ _writer.TrimNewLines();
+ _writer.AppendRaw(_end);
+ }
+
+ if (_newLine)
+ {
+ _writer.Line();
+ }
+ }
+ }
+ }
+
+ private void TrimNewLines()
+ {
+ while (PreviousLine.SequenceEqual(_newLine) &&
+ CurrentLine.IsEmpty)
+ {
+ _length--;
+ }
+ }
+
+ private void PopScope(CodeWriterScope expected)
+ {
+ var actual = _scopes.Pop();
+ Debug.Assert(actual == expected);
+ }
+
+ private int? FindLastNonWhitespaceCharacterIndex()
+ {
+ var text = WrittenText;
+ for (int i = text.Length - 1; i >= 0; i--)
+ {
+ if (char.IsWhiteSpace(text[i]))
+ {
+ continue;
+ }
+
+ return i;
+ }
+
+ return null;
+ }
+
+ public void RemoveTrailingCharacter()
+ {
+ int? lastCharIndex = FindLastNonWhitespaceCharacterIndex();
+ if (lastCharIndex.HasValue)
+ {
+ _length = lastCharIndex.Value;
+ }
+ }
+
+ public void RemoveTrailingComma()
+ {
+ int? lastCharIndex = FindLastNonWhitespaceCharacterIndex();
+ if (lastCharIndex.HasValue && WrittenText[lastCharIndex.Value] == ',')
+ {
+ _length = lastCharIndex.Value;
+ }
+ }
+
+ public CodeWriterScope AmbientScope()
+ {
+ var codeWriterScope = new CodeWriterScope(this, null, false);
+ _scopes.Push(codeWriterScope);
+ return codeWriterScope;
+ }
+
+ public virtual void Append(CodeWriterDeclaration declaration)
+ {
+ Identifier(declaration.ActualName);
+ }
+
+ internal void WriteTypeModifiers(TypeSignatureModifiers modifiers)
+ {
+ this.AppendRawIf("public ", modifiers.HasFlag(TypeSignatureModifiers.Public))
+ .AppendRawIf("internal ", modifiers.HasFlag(TypeSignatureModifiers.Internal))
+ .AppendRawIf("private ", modifiers.HasFlag(TypeSignatureModifiers.Private))
+ .AppendRawIf("static ", modifiers.HasFlag(TypeSignatureModifiers.Static))
+ .AppendRawIf("sealed ", modifiers.HasFlag(TypeSignatureModifiers.Sealed))
+ .AppendRawIf("partial ", modifiers.HasFlag(TypeSignatureModifiers.Partial)); // partial must be the last to write otherwise compiler will complain
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Writers/CodeWriterDeclaration.cs b/logger/autorest.csharp/common/Generation/Writers/CodeWriterDeclaration.cs
new file mode 100644
index 0000000..c2c5a6d
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Writers/CodeWriterDeclaration.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+
+namespace AutoRest.CSharp.Generation.Writers
+{
+ internal class CodeWriterDeclaration
+ {
+ private string? _actualName;
+ private string? _debuggerName;
+
+ public CodeWriterDeclaration(string name)
+ {
+ RequestedName = name;
+ }
+
+ public string RequestedName { get; }
+
+ public string ActualName => _actualName ?? _debuggerName ?? throw new InvalidOperationException($"Declaration {RequestedName} is not initialized");
+
+ internal void SetActualName(string? actualName)
+ {
+ if (_actualName != null && actualName != null)
+ {
+ throw new InvalidOperationException($"Declaration {_actualName} already initialized, can't initialize it with {actualName} name.");
+ }
+
+ _actualName = actualName;
+ }
+
+ internal void SetDebuggerName(string? debuggerName)
+ {
+ if (_actualName == null)
+ {
+ _debuggerName = debuggerName;
+ }
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Writers/CodeWriterExtensions.cs b/logger/autorest.csharp/common/Generation/Writers/CodeWriterExtensions.cs
new file mode 100644
index 0000000..efaeba1
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Writers/CodeWriterExtensions.cs
@@ -0,0 +1,824 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using AutoRest.CSharp.Common.Output.Builders;
+using AutoRest.CSharp.Common.Output.Expressions.KnownValueExpressions;
+using AutoRest.CSharp.Common.Output.Expressions.Statements;
+using AutoRest.CSharp.Common.Output.Expressions.ValueExpressions;
+using AutoRest.CSharp.Common.Output.Models;
+using AutoRest.CSharp.Generation.Types;
+using AutoRest.CSharp.Output.Models;
+using AutoRest.CSharp.Output.Models.Requests;
+using AutoRest.CSharp.Output.Models.Serialization;
+using AutoRest.CSharp.Output.Models.Serialization.Json;
+using AutoRest.CSharp.Output.Models.Serialization.Xml;
+using AutoRest.CSharp.Output.Models.Shared;
+using AutoRest.CSharp.Output.Models.Types;
+using AutoRest.CSharp.Utilities;
+using Azure;
+using Azure.Core.Pipeline;
+using static AutoRest.CSharp.Output.Models.MethodSignatureModifiers;
+
+namespace AutoRest.CSharp.Generation.Writers
+{
+ internal static class CodeWriterExtensions
+ {
+ public static CodeWriter AppendIf(this CodeWriter writer, FormattableString formattableString, bool condition)
+ {
+ if (condition)
+ {
+ writer.Append(formattableString);
+ }
+
+ return writer;
+ }
+
+ public static CodeWriter AppendRawIf(this CodeWriter writer, string str, bool condition)
+ {
+ if (condition)
+ {
+ writer.AppendRaw(str);
+ }
+
+ return writer;
+ }
+
+ public static CodeWriter LineIf(this CodeWriter writer, FormattableString formattableString, bool condition)
+ {
+ if (condition)
+ {
+ writer.Line(formattableString);
+ }
+
+ return writer;
+ }
+
+ public static CodeWriter LineRawIf(this CodeWriter writer, string str, bool condition)
+ {
+ if (condition)
+ {
+ writer.LineRaw(str);
+ }
+
+ return writer;
+ }
+
+ public static CodeWriter AppendNullableValue(this CodeWriter writer, CSharpType type)
+ {
+ if (type.IsNullable && type.IsValueType)
+ {
+ writer.Append($".Value");
+ }
+
+ return writer;
+ }
+
+
+ public static CodeWriter WriteField(this CodeWriter writer, FieldDeclaration field, bool declareInCurrentScope = true)
+ {
+ if (field.Description != null)
+ {
+ writer.Line().WriteXmlDocumentationSummary(field.Description);
+ }
+
+ var modifiers = field.Modifiers;
+
+ if (field.WriteAsProperty)
+ {
+ writer
+ .AppendRaw(modifiers.HasFlag(FieldModifiers.Public) ? "public " : (modifiers.HasFlag(FieldModifiers.Internal) ? "internal " : "private "));
+ }
+ else
+ {
+ writer
+ .AppendRaw(modifiers.HasFlag(FieldModifiers.Public) ? "public " : (modifiers.HasFlag(FieldModifiers.Internal) ? "internal " : "private "))
+ .AppendRawIf("const ", modifiers.HasFlag(FieldModifiers.Const))
+ .AppendRawIf("static ", modifiers.HasFlag(FieldModifiers.Static))
+ .AppendRawIf("readonly ", modifiers.HasFlag(FieldModifiers.ReadOnly));
+ }
+
+ if (declareInCurrentScope)
+ {
+ writer.Append($"{field.Type} {field.Declaration:D}");
+ }
+ else
+ {
+ writer.Append($"{field.Type} {field.Declaration:I}");
+ }
+
+ if (field.WriteAsProperty)
+ {
+ writer.AppendRaw(modifiers.HasFlag(FieldModifiers.ReadOnly) ? "{ get; }" : "{ get; set; }");
+ }
+
+ if (field.InitializationValue != null &&
+ (modifiers.HasFlag(FieldModifiers.Const) || modifiers.HasFlag(FieldModifiers.Static)))
+ {
+ field.InitializationValue.Write(writer.AppendRaw(" = "));
+ return writer.Line($";");
+ }
+
+ return field.WriteAsProperty ? writer.Line() : writer.Line($";");
+ }
+
+ public static CodeWriter WriteFieldDeclarations(this CodeWriter writer, IEnumerable fields)
+ {
+ foreach (var field in fields)
+ {
+ writer.WriteField(field, declareInCurrentScope: false);
+ }
+
+ return writer.Line();
+ }
+
+ public static IDisposable WriteMethodDeclaration(this CodeWriter writer, MethodSignatureBase methodBase, params string[] disabledWarnings)
+ {
+ var outerScope = writer.WriteMethodDeclarationNoScope(methodBase, disabledWarnings);
+ writer.Line();
+ var innerScope = writer.Scope();
+ return Disposable.Create(() =>
+ {
+ innerScope.Dispose();
+ outerScope.Dispose();
+ });
+ }
+
+ private static IDisposable WriteMethodDeclarationNoScope(this CodeWriter writer, MethodSignatureBase methodBase, params string[] disabledWarnings)
+ {
+ if (methodBase.Attributes is { } attributes)
+ {
+ foreach (var attribute in attributes)
+ {
+ if (attribute.Arguments.Any())
+ {
+ writer.Append($"[{attribute.Type}(");
+ foreach (var argument in attribute.Arguments)
+ {
+ argument.Write(writer);
+ }
+ writer.RemoveTrailingComma();
+ writer.LineRaw(")]");
+ }
+ else
+ {
+ writer.Line($"[{attribute.Type}]");
+ }
+ }
+ }
+
+ foreach (var disabledWarning in disabledWarnings)
+ {
+ writer.Line($"#pragma warning disable {disabledWarning}");
+ }
+
+ writer
+ .AppendRawIf("public ", methodBase.Modifiers.HasFlag(Public))
+ .AppendRawIf("internal ", methodBase.Modifiers.HasFlag(Internal))
+ .AppendRawIf("protected ", methodBase.Modifiers.HasFlag(Protected))
+ .AppendRawIf("private ", methodBase.Modifiers.HasFlag(Private))
+ .AppendRawIf("static ", methodBase.Modifiers.HasFlag(Static));
+
+ if (methodBase is MethodSignature method)
+ {
+ writer
+ .AppendRawIf("virtual ", methodBase.Modifiers.HasFlag(Virtual))
+ .AppendRawIf("override ", methodBase.Modifiers.HasFlag(Override))
+ .AppendRawIf("new ", methodBase.Modifiers.HasFlag(New))
+ .AppendRawIf("async ", methodBase.Modifiers.HasFlag(Async));
+
+ if (method.ReturnType != null)
+ {
+ writer.Append($"{method.ReturnType} ");
+ }
+ else
+ {
+ writer.AppendRaw("void ");
+ }
+
+ if (method.ExplicitInterface is not null)
+ {
+ writer.Append($"{method.ExplicitInterface}.");
+ }
+
+ writer.Append($"{methodBase.Name}");
+
+ if (method?.GenericArguments != null)
+ {
+ writer.AppendRaw("<");
+ foreach (var argument in method.GenericArguments)
+ {
+ writer.Append($"{argument:D},");
+ }
+ writer.RemoveTrailingComma();
+ writer.AppendRaw(">");
+ }
+ }
+ else
+ {
+ writer.Append($"{methodBase.Name}");
+ }
+
+ writer
+ .AppendRaw("(")
+ .AppendRawIf("this ", methodBase.Modifiers.HasFlag(Extension));
+
+ var outerScope = writer.AmbientScope();
+
+ foreach (var parameter in methodBase.Parameters)
+ {
+ writer.WriteParameter(parameter);
+ }
+
+ writer.RemoveTrailingComma();
+ writer.Append($")");
+
+ if (methodBase is MethodSignature { GenericParameterConstraints: { } constraints })
+ {
+ writer.Line();
+ foreach (var constraint in constraints)
+ {
+ constraint.Write(writer);
+ writer.AppendRaw(" ");
+ }
+ }
+
+ if (methodBase is ConstructorSignature { Initializer: { } } constructor)
+ {
+ var (isBase, arguments) = constructor.Initializer;
+
+ if (!isBase || arguments.Any())
+ {
+ writer.AppendRaw(isBase ? ": base(" : ": this(");
+ foreach (var argument in arguments)
+ {
+ argument.Write(writer);
+ writer.AppendRaw(", ");
+ }
+ writer.RemoveTrailingComma();
+ writer.AppendRaw(")");
+ }
+ }
+
+ foreach (var disabledWarning in disabledWarnings)
+ {
+ writer.Line();
+ writer.Append($"#pragma warning restore {disabledWarning}");
+ }
+
+ return outerScope;
+ }
+
+ public static CodeWriter WriteMethodDocumentation(this CodeWriter writer, MethodSignatureBase methodBase)
+ {
+ if (methodBase.IsRawSummaryText)
+ {
+ return writer.WriteRawXmlDocumentation(methodBase.Description);
+ }
+
+ if (methodBase.NonDocumentComment is { } comment)
+ {
+ writer.Line($"// {comment}");
+ }
+
+ if (methodBase.SummaryText is { } summaryText)
+ {
+ writer.WriteXmlDocumentationSummary(summaryText);
+ }
+
+ return writer.WriteMethodDocumentationSignature(methodBase);
+ }
+
+ public static CodeWriter WriteMethodDocumentation(this CodeWriter writer, MethodSignatureBase methodBase, FormattableString? summaryText = null)
+ {
+ return writer
+ .WriteXmlDocumentationSummary(summaryText ?? methodBase.SummaryText)
+ .WriteMethodDocumentationSignature(methodBase);
+ }
+
+ public static CodeWriter WriteMethodDocumentationSignature(this CodeWriter writer, MethodSignatureBase methodBase)
+ {
+ writer.WriteXmlDocumentationParameters(methodBase.Modifiers.HasFlag(Public) ? methodBase.Parameters : methodBase.Parameters.Where(p => p.Description is not null));
+
+ writer.WriteXmlDocumentationRequiredParametersException(methodBase.Parameters);
+ writer.WriteXmlDocumentationNonEmptyParametersException(methodBase.Parameters);
+ if (methodBase is MethodSignature { ReturnDescription: { } } method)
+ {
+ writer.WriteXmlDocumentationReturns(method.ReturnDescription);
+ }
+
+ return writer;
+ }
+
+ public static void WriteParameter(this CodeWriter writer, Parameter clientParameter)
+ {
+ if (clientParameter.Attributes.Any())
+ {
+ writer.AppendRaw("[");
+ foreach (var attribute in clientParameter.Attributes)
+ {
+ writer.Append($"{attribute.Type}, ");
+ }
+ writer.RemoveTrailingComma();
+ writer.AppendRaw("]");
+ }
+
+ writer.AppendRawIf("out ", clientParameter.IsOut);
+ writer.AppendRawIf("ref ", clientParameter.IsRef);
+
+ writer.Append($"{clientParameter.Type} {clientParameter.Name:D}");
+ if (clientParameter.DefaultValue != null)
+ {
+ var defaultValue = clientParameter.DefaultValue.Value;
+ if (defaultValue.IsNewInstanceSentinel && defaultValue.Type.IsValueType || clientParameter.IsApiVersionParameter && clientParameter.Initializer != null)
+ {
+ writer.Append($" = default");
+ }
+ else
+ {
+ writer.Append($" = {clientParameter.DefaultValue.Value.GetConstantFormattable()}");
+ }
+ }
+
+ writer.AppendRaw(",");
+ }
+
+ public static CodeWriter WriteParametersValidation(this CodeWriter writer, IEnumerable parameters)
+ {
+ foreach (Parameter parameter in parameters)
+ {
+ writer.WriteParameterValidation(parameter);
+ }
+
+ writer.Line();
+ return writer;
+ }
+
+ private static CodeWriter WriteParameterValidation(this CodeWriter writer, Parameter parameter)
+ {
+ if (parameter.Validation == ValidationType.None && parameter.Initializer != null)
+ {
+ return writer.Line($"{parameter.Name:I} ??= {parameter.Initializer};");
+ }
+
+ var validationStatement = Snippets.Argument.ValidateParameter(parameter);
+
+ validationStatement.Write(writer);
+
+ return writer;
+ }
+
+ public static CodeWriter WriteParameterNullChecks(this CodeWriter writer, IReadOnlyCollection parameters)
+ {
+ foreach (Parameter parameter in parameters)
+ {
+ writer.WriteVariableAssignmentWithNullCheck(parameter.Name, parameter);
+ }
+
+ writer.Line();
+ return writer;
+ }
+
+ private static Dictionary requestConditionHeaderNames = new Dictionary {
+ {RequestConditionHeaders.None, "" },
+ {RequestConditionHeaders.IfMatch, "If-Match" },
+ {RequestConditionHeaders.IfNoneMatch, "If-None-Match" },
+ {RequestConditionHeaders.IfModifiedSince, "If-Modified-Since" },
+ {RequestConditionHeaders.IfUnmodifiedSince, "If-Unmodified-Since" }
+ };
+
+ private static Dictionary requestConditionFieldNames = new Dictionary {
+ {RequestConditionHeaders.None, "" },
+ {RequestConditionHeaders.IfMatch, "IfMatch" },
+ {RequestConditionHeaders.IfNoneMatch, "IfNoneMatch" },
+ {RequestConditionHeaders.IfModifiedSince, "IfModifiedSince" },
+ {RequestConditionHeaders.IfUnmodifiedSince, "IfUnmodifiedSince" }
+ };
+
+ public static CodeWriter WriteRequestConditionParameterChecks(this CodeWriter writer, IReadOnlyCollection parameters, RequestConditionHeaders requestConditionFlag)
+ {
+ foreach (Parameter parameter in parameters)
+ {
+ if (parameter.Type.Equals(typeof(RequestConditions)))
+ {
+ string nullableFlag = (parameter.Type.IsNullable) ? "?" : "";
+ foreach (RequestConditionHeaders val in Enum.GetValues(typeof(RequestConditionHeaders)).Cast())
+ {
+ if (val != RequestConditionHeaders.None && !requestConditionFlag.HasFlag(val))
+ {
+ using (writer.Scope($"if ({parameter.Name:I}{nullableFlag}.{requestConditionFieldNames[val]} is not null)"))
+ {
+ writer.Line($"throw new {typeof(ArgumentNullException)}(nameof({parameter.Name:I}), \"Service does not support the {requestConditionHeaderNames[val]} header for this operation.\");");
+ }
+ }
+ }
+ }
+ }
+ return writer;
+ }
+
+ public static CodeWriter.CodeWriterScope WriteUsingStatement(this CodeWriter writer, string variableName, bool asyncCall, FormattableString asyncMethodName, FormattableString syncMethodName, FormattableString parameters, out CodeWriterDeclaration variable)
+ {
+ variable = new CodeWriterDeclaration(variableName);
+ return writer.Scope($"using (var {variable:D} = {GetMethodCallFormattableString(asyncCall, asyncMethodName, syncMethodName, parameters)})");
+ }
+
+ public static CodeWriter WriteMethodCall(this CodeWriter writer, bool asyncCall, FormattableString methodName, FormattableString parameters)
+ => writer.WriteMethodCall(asyncCall, methodName, methodName, parameters, false);
+
+ public static CodeWriter WriteMethodCall(this CodeWriter writer, bool asyncCall, FormattableString asyncMethodName, FormattableString syncMethodName, FormattableString parameters, bool writeLine = true)
+ => writer.Append(GetMethodCallFormattableString(asyncCall, asyncMethodName, syncMethodName, parameters)).LineRawIf(";", writeLine);
+
+ public static CodeWriter WriteMethodCall(this CodeWriter writer, MethodSignature method, IEnumerable parameters, bool asyncCall)
+ {
+ var parametersFs = parameters.ToArray().Join(", ");
+ if (asyncCall)
+ {
+ return writer.Append($"await {method.WithAsync(true).Name}({parametersFs}).ConfigureAwait(false)");
+ }
+
+ return writer.Append($"{method.WithAsync(false).Name}({parametersFs})");
+ }
+
+ private static FormattableString GetMethodCallFormattableString(bool asyncCall, FormattableString asyncMethodName, FormattableString syncMethodName, FormattableString parameters)
+ => asyncCall ? (FormattableString)$"await {asyncMethodName}({parameters}).ConfigureAwait(false)" : $"{syncMethodName}({parameters})";
+
+ public static void WriteVariableAssignmentWithNullCheck(this CodeWriter writer, string variableName, Parameter parameter)
+ {
+ // Temporary check to minimize amount of changes in existing generated code
+ var assignToSelf = parameter.Name == variableName;
+ if (parameter.Initializer != null)
+ {
+ if (assignToSelf)
+ {
+ writer.Line($"{variableName:I} ??= {parameter.Initializer};");
+ }
+ else
+ {
+ writer.Line($"{variableName:I} = {parameter.Name:I} ?? {parameter.Initializer};");
+ }
+ }
+ else if (parameter.Validation != ValidationType.None)
+ {
+ // Temporary check to minimize amount of changes in existing generated code
+ if (assignToSelf)
+ {
+ using (writer.Scope($"if ({parameter.Name:I} == null)"))
+ {
+ writer.Line($"throw new {typeof(ArgumentNullException)}(nameof({parameter.Name:I}));");
+ }
+ }
+ else
+ {
+ writer.Line($"{variableName:I} = {parameter.Name:I} ?? throw new {typeof(ArgumentNullException)}(nameof({parameter.Name:I}));");
+ }
+ }
+ else if (!assignToSelf)
+ {
+ writer.Line($"{variableName:I} = {parameter.Name:I};");
+ }
+ }
+
+ public static CodeWriter WriteConstant(this CodeWriter writer, Constant constant) => writer.Append(constant.GetConstantFormattable());
+
+ public static void WriteDeserializationForMethods(this CodeWriter writer, ObjectSerialization serialization, bool async, ValueExpression? variable, FormattableString streamFormattable, CSharpType? type)
+ {
+ var streamExpression = new StreamExpression(new FormattableStringToExpression(streamFormattable));
+ switch (serialization)
+ {
+ case JsonSerialization jsonSerialization:
+ JsonSerializationMethodsBuilder.BuildDeserializationForMethods(jsonSerialization, async, variable, streamExpression, type is not null && type.Equals(typeof(BinaryData)), null).Write(writer);
+ break;
+ case XmlElementSerialization xmlSerialization:
+ XmlSerializationMethodsBuilder.BuildDeserializationForMethods(xmlSerialization, variable, streamExpression).Write(writer);
+ break;
+ default:
+ throw new NotImplementedException(serialization.ToString());
+ }
+ }
+
+ public static CodeWriter AppendEnumToString(this CodeWriter writer, EnumType enumType)
+ {
+ new EnumExpression(enumType, new ValueExpression()).ToSerial().Write(writer);
+ return writer;
+ }
+
+ public static CodeWriter AppendEnumFromString(this CodeWriter writer, EnumType enumType, FormattableString value)
+ {
+ if (enumType.IsExtensible)
+ {
+ writer.Append($"new {enumType.Type}({value})");
+ }
+ else
+ {
+ writer.UseNamespace(enumType.Type.Namespace);
+ writer.Append($"{value}.To{enumType.Declaration.Name}()");
+ }
+
+ return writer;
+ }
+
+ public static CodeWriter WriteReferenceOrConstant(this CodeWriter writer, ReferenceOrConstant value)
+ => writer.Append(value.GetReferenceOrConstantFormattable());
+
+ public static CodeWriter WriteInitialization(
+ this CodeWriter writer,
+ Action valueCallback,
+ TypeProvider objectType,
+ ObjectTypeConstructor constructor,
+ IEnumerable initializers)
+ {
+ var initializersSet = initializers.ToHashSet();
+
+ // Find longest satisfiable ctor
+ List selectedCtorInitializers = constructor.Signature.Parameters
+ .Select(constructor.FindPropertyInitializedByParameter)
+ .Select(property => initializersSet.SingleOrDefault(i => i.Name == property?.Declaration.Name && Equals(i.Type, property.Declaration.Type)))
+ .ToList();
+
+ // Checks if constructor parameters can be satisfied by the provided initializer list
+ Debug.Assert(!selectedCtorInitializers.Contains(default));
+
+ // Find properties that would have to be initialized using a foreach loop
+ var collectionInitializers = initializersSet
+ .Except(selectedCtorInitializers)
+ .Where(i => i is { IsReadOnly: true, Type.IsCollection: true })
+ .ToArray();
+
+ // Find properties that would have to be initialized via property initializers
+ var restOfInitializers = initializersSet
+ .Except(selectedCtorInitializers)
+ .Except(collectionInitializers)
+ .ToArray();
+
+ var constructorParameters = selectedCtorInitializers
+ .Select(pi => $"{pi.Value}{GetConversion(writer, pi.ValueType!, pi.Type)}")
+ .ToArray()
+ .Join(", ");
+
+ var propertyInitializers = restOfInitializers
+ .Select(pi => $"{pi.Name} = {pi.Value}{GetConversion(writer, pi.ValueType!, pi.Type)}")
+ .ToArray()
+ .Join(",\n ");
+
+ var objectInitializerFormattable = restOfInitializers.Any()
+ ? $"new {objectType.Type}({constructorParameters}) {{\n{propertyInitializers}\n}}"
+ : (FormattableString)$"new {objectType.Type}({constructorParameters})";
+
+ if (collectionInitializers.Any())
+ {
+ var modelVariable = new CodeWriterDeclaration(objectType.Declaration.Name.ToVariableName());
+ writer.Line($"{objectType.Type} {modelVariable:D} = {objectInitializerFormattable};");
+
+ // Writes the:
+ // foreach (var value in param)
+ // {
+ // model.CollectionProperty = value;
+ // }
+ foreach (var propertyInitializer in collectionInitializers)
+ {
+ var valueVariable = new CodeWriterDeclaration("value");
+ using (writer.Scope($"if ({propertyInitializer.Value} != null)"))
+ {
+ using (writer.Scope($"foreach (var {valueVariable:D} in {propertyInitializer.Value})"))
+ {
+ writer.Append($"{modelVariable:I}.{propertyInitializer.Name}.Add({valueVariable});");
+ }
+ }
+ }
+
+ valueCallback($"{modelVariable:I}");
+ }
+ else
+ {
+ valueCallback(objectInitializerFormattable);
+ }
+
+
+ return writer;
+ }
+
+ public static CodeWriter WriteConversion(this CodeWriter writer, CSharpType from, CSharpType to)
+ {
+ if (CSharpType.RequiresToList(from, to))
+ {
+ writer.UseNamespace(typeof(Enumerable).Namespace!);
+ return writer.AppendRaw(from.IsNullable ? "?.ToList()" : ".ToList()");
+ }
+
+ return writer;
+ }
+
+ internal static string GetConversion(CodeWriter writer, CSharpType from, CSharpType to)
+ {
+ if (CSharpType.RequiresToList(from, to))
+ {
+ writer.UseNamespace(typeof(Enumerable).Namespace!);
+ return from.IsNullable ? "?.ToList()" : ".ToList()";
+ }
+
+ return string.Empty;
+ }
+
+ public static IDisposable WriteCommonMethodWithoutValidation(this CodeWriter writer, MethodSignature signature, FormattableString? returnDescription, bool isAsync, bool isPublicType)
+ {
+ writer.WriteXmlDocumentationSummary(signature.Description);
+ writer.WriteXmlDocumentationParameters(signature.Parameters);
+ if (isPublicType)
+ {
+ writer.WriteXmlDocumentationNonEmptyParametersException(signature.Parameters);
+ writer.WriteXmlDocumentationRequiredParametersException(signature.Parameters);
+ }
+
+ FormattableString? returnDesc = returnDescription ?? signature.ReturnDescription;
+ if (returnDesc is not null)
+ writer.WriteXmlDocumentationReturns(returnDesc);
+
+ return writer.WriteMethodDeclaration(signature.WithAsync(isAsync));
+ }
+
+ public static IDisposable WriteCommonMethod(this CodeWriter writer, MethodSignature signature, FormattableString? returnDescription, bool isAsync, bool isPublicType, bool skipValidation = false)
+ {
+ var scope = WriteCommonMethodWithoutValidation(writer, signature, returnDescription, isAsync, isPublicType);
+ if (isPublicType && !skipValidation)
+ writer.WriteParametersValidation(signature.Parameters);
+
+ return scope;
+ }
+
+ public static CodeWriter WriteEnableHttpRedirectIfNecessary(this CodeWriter writer, RestClientMethod restClientMethod, TypedValueExpression messageVariable)
+ {
+ if (restClientMethod.ShouldEnableRedirect)
+ {
+ new InvokeStaticMethodStatement(typeof(RedirectPolicy), nameof(RedirectPolicy.SetAllowAutoRedirect), messageVariable, Snippets.True).Write(writer);
+ }
+ return writer;
+ }
+
+ public static void WriteMethod(this CodeWriter writer, Method method)
+ {
+ if (method.Body is { } body)
+ {
+ using (writer.WriteMethodDeclaration(method.Signature))
+ {
+ body.Write(writer);
+ }
+ }
+ else if (method.BodyExpression is { } expression)
+ {
+ using (writer.WriteMethodDeclarationNoScope(method.Signature))
+ {
+ writer.AppendRaw(" => ");
+ expression.Write(writer);
+ writer.LineRaw(";");
+ }
+ }
+
+ writer.Line();
+ }
+
+ public static void WriteProperty(this CodeWriter writer, PropertyDeclaration property)
+ {
+ if (property.Description is not null)
+ {
+ writer.Line().WriteXmlDocumentationSummary(property.Description);
+ }
+
+ if (property.Exceptions is not null)
+ {
+ foreach (var (exceptionType, description) in property.Exceptions)
+ {
+ writer.WriteXmlDocumentationException(exceptionType, description);
+ }
+ }
+
+ var modifiers = property.Modifiers;
+ writer.AppendRawIf("public ", modifiers.HasFlag(MethodSignatureModifiers.Public))
+ .AppendRawIf("protected ", modifiers.HasFlag(MethodSignatureModifiers.Protected))
+ .AppendRawIf("internal ", modifiers.HasFlag(MethodSignatureModifiers.Internal))
+ .AppendRawIf("private ", modifiers.HasFlag(MethodSignatureModifiers.Private))
+ .AppendRawIf("override ", modifiers.HasFlag(MethodSignatureModifiers.Override))
+ .AppendRawIf("static ", modifiers.HasFlag(MethodSignatureModifiers.Static))
+ .AppendRawIf("virtual ", modifiers.HasFlag(MethodSignatureModifiers.Virtual)); // property does not support other modifiers, here we just ignore them if any
+
+ writer.Append($"{property.PropertyType} ");
+ if (property is IndexerDeclaration indexer)
+ {
+ writer.Append($"this[{indexer.IndexerParameter.Type} {indexer.IndexerParameter.Name}]");
+ }
+ else
+ {
+ if (property.ExplicitInterface is not null)
+ {
+ writer.Append($"{property.ExplicitInterface}.");
+ }
+ writer.Append($"{property.Declaration:I}"); // the declaration order here is quite anonying - we might need to assign the values to those properties in other places before these are written
+ }
+
+ switch (property.PropertyBody)
+ {
+ case ExpressionPropertyBody(var expression):
+ expression.Write(writer.AppendRaw(" => "));
+ writer.AppendRaw(";");
+ break;
+ case AutoPropertyBody(var hasSetter, var setterModifiers, var initialization):
+ writer.AppendRaw("{ get; ");
+ if (hasSetter)
+ {
+ WritePropertyAccessorModifiers(writer, setterModifiers);
+ writer.AppendRaw(" set; ");
+ }
+ writer.AppendRaw("}");
+ if (initialization is not null)
+ {
+ initialization.Write(writer.AppendRaw(" = "));
+ }
+ break;
+ case MethodPropertyBody(var getter, var setter, var setterModifiers):
+ writer.LineRaw("{");
+ // write getter
+ WriteMethodPropertyAccessor(writer, "get", getter);
+ // write setter
+ if (setter is not null)
+ {
+ WriteMethodPropertyAccessor(writer, "set", setter, setterModifiers);
+ }
+ writer.AppendRaw("}");
+ break;
+ default:
+ throw new InvalidOperationException($"Unhandled property body type {property.PropertyBody}");
+ }
+
+ writer.Line();
+
+ static void WriteMethodPropertyAccessor(CodeWriter writer, string name, MethodBodyStatement body, MethodSignatureModifiers modifiers = MethodSignatureModifiers.None)
+ {
+ WritePropertyAccessorModifiers(writer, modifiers);
+ writer.LineRaw(name)
+ .LineRaw("{");
+ using (writer.AmbientScope())
+ {
+ body.Write(writer);
+ }
+ writer.LineRaw("}");
+ }
+
+ static void WritePropertyAccessorModifiers(CodeWriter writer, MethodSignatureModifiers modifiers)
+ {
+ writer.AppendRawIf("protected ", modifiers.HasFlag(MethodSignatureModifiers.Protected))
+ .AppendRawIf("internal ", modifiers.HasFlag(MethodSignatureModifiers.Internal))
+ .AppendRawIf("private ", modifiers.HasFlag(MethodSignatureModifiers.Private));
+ }
+ }
+
+ public static void WriteTypeArguments(this CodeWriter writer, IEnumerable? typeArguments)
+ {
+ if (typeArguments is null)
+ {
+ return;
+ }
+
+ writer.AppendRaw("<");
+ foreach (var argument in typeArguments)
+ {
+ writer.Append($"{argument}, ");
+ }
+
+ writer.RemoveTrailingComma();
+ writer.AppendRaw(">");
+ }
+
+ public static void WriteArguments(this CodeWriter writer, IEnumerable arguments, bool useSingleLine = true)
+ {
+ if (useSingleLine)
+ {
+ writer.AppendRaw("(");
+ foreach (var argument in arguments)
+ {
+ argument.Write(writer);
+ writer.AppendRaw(", ");
+ }
+
+ writer.RemoveTrailingComma();
+ writer.AppendRaw(")");
+ }
+ else
+ {
+ writer.LineRaw("(");
+ foreach (var argument in arguments)
+ {
+ writer.AppendRaw("\t");
+ argument.Write(writer);
+ writer.LineRaw(",");
+ }
+
+ writer.RemoveTrailingCharacter();
+ writer.RemoveTrailingComma();
+ writer.AppendRaw(")");
+ }
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Writers/CodeWriterScopeDeclarations.cs b/logger/autorest.csharp/common/Generation/Writers/CodeWriterScopeDeclarations.cs
new file mode 100644
index 0000000..60f16c3
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Writers/CodeWriterScopeDeclarations.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+
+namespace AutoRest.CSharp.Generation.Writers
+{
+ internal class CodeWriterScopeDeclarations
+ {
+ public IReadOnlyList Names { get; }
+
+ public CodeWriterScopeDeclarations(IEnumerable declarations)
+ {
+ var names = new List();
+ foreach (var declaration in declarations)
+ {
+ declaration.SetActualName(declaration.RequestedName);
+ names.Add(declaration.ActualName);
+ }
+
+ Names = names;
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Writers/DebuggerCodeWriter.cs b/logger/autorest.csharp/common/Generation/Writers/DebuggerCodeWriter.cs
new file mode 100644
index 0000000..16a7d5b
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Writers/DebuggerCodeWriter.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+
+namespace AutoRest.CSharp.Generation.Writers
+{
+ internal sealed class DebuggerCodeWriter : CodeWriter, IDisposable
+ {
+ private readonly List _declarations;
+
+ public DebuggerCodeWriter()
+ {
+ _declarations = new List();
+ }
+
+ public override CodeWriter Declaration(CodeWriterDeclaration declaration)
+ {
+ declaration.SetDebuggerName(GetTemporaryVariable(declaration.RequestedName));
+ _declarations.Add(declaration);
+ return Declaration(declaration.ActualName);
+ }
+
+ public void Dispose()
+ {
+ foreach (var declaration in _declarations)
+ {
+ declaration.SetDebuggerName(null);
+ }
+ }
+
+ public override void Append(CodeWriterDeclaration declaration)
+ {
+ try
+ {
+ Identifier(declaration.ActualName);
+ }
+ catch (InvalidOperationException)
+ {
+ Identifier(declaration.RequestedName);
+ }
+ }
+
+ public override string ToString() => ToString(false);
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Writers/DiagnosticScopeWriter.cs b/logger/autorest.csharp/common/Generation/Writers/DiagnosticScopeWriter.cs
new file mode 100644
index 0000000..fa60a2c
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Writers/DiagnosticScopeWriter.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+
+using System;
+using AutoRest.CSharp.Output.Models.Requests;
+using Azure.Core.Pipeline;
+
+namespace AutoRest.CSharp.Generation.Writers
+{
+ internal static class DiagnosticScopeWriter
+ {
+ public static IDisposable WriteDiagnosticScope(this CodeWriter writer, Diagnostic diagnostic, Reference clientDiagnostics)
+ {
+ var scopeVariable = new CodeWriterDeclaration("scope");
+ writer.Line($"using var {scopeVariable:D} = {clientDiagnostics.GetReferenceFormattable()}.{nameof(ClientDiagnostics.CreateScope)}({diagnostic.ScopeName:L});");
+ foreach (DiagnosticAttribute diagnosticScopeAttributes in diagnostic.Attributes)
+ {
+ writer.Append($"{scopeVariable}.AddAttribute({diagnosticScopeAttributes.Name:L},");
+ writer.WriteReferenceOrConstant(diagnosticScopeAttributes.Value);
+ writer.Line($");");
+ }
+
+ writer.Line($"{scopeVariable}.Start();");
+ return new DiagnosticScope(writer.Scope($"try"), scopeVariable, writer);
+ }
+
+ private class DiagnosticScope : IDisposable
+ {
+ private readonly CodeWriter.CodeWriterScope _scope;
+ private readonly CodeWriterDeclaration _scopeVariable;
+ private readonly CodeWriter _writer;
+
+ public DiagnosticScope(CodeWriter.CodeWriterScope scope, CodeWriterDeclaration scopeVariable, CodeWriter writer)
+ {
+ _scope = scope;
+ _scopeVariable = scopeVariable;
+ _writer = writer;
+ }
+
+ public void Dispose()
+ {
+ _scope.Dispose();
+ using (_writer.Scope($"catch ({typeof(Exception)} e)"))
+ {
+ _writer.Line($"{_scopeVariable}.Failed(e);");
+ _writer.Line($"throw;");
+ }
+ }
+ }
+ }
+}
diff --git a/logger/autorest.csharp/common/Generation/Writers/DocumentationWriterExtensions.cs b/logger/autorest.csharp/common/Generation/Writers/DocumentationWriterExtensions.cs
new file mode 100644
index 0000000..e2c53a6
--- /dev/null
+++ b/logger/autorest.csharp/common/Generation/Writers/DocumentationWriterExtensions.cs
@@ -0,0 +1,145 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using AutoRest.CSharp.Common.Input;
+using AutoRest.CSharp.Generation.Types;
+using AutoRest.CSharp.Output.Models;
+using AutoRest.CSharp.Output.Models.Shared;
+using static AutoRest.CSharp.Output.Models.Shared.ValidationType;
+
+namespace AutoRest.CSharp.Generation.Writers
+{
+ internal static class DocumentationWriterExtensions
+ {
+ public static CodeWriter WriteXmlDocumentationSummary(this CodeWriter writer, FormattableString? text)
+ {
+ return writer.WriteXmlDocumentation("summary", text);
+ }
+
+ public static CodeWriter WriteXmlDocumentation(this CodeWriter writer, string tag, FormattableString? text)
+ {
+ return writer.WriteDocumentationLines($"<{tag}>", $"{tag}>", text);
+ }
+
+ public static CodeWriter WriteXmlDocumentationParameters(this CodeWriter writer, IEnumerable