diff --git a/AutoRest.sln.DotSettings b/AutoRest.sln.DotSettings
index a16e1edd39e62..0a190f6536fe7 100644
--- a/AutoRest.sln.DotSettings
+++ b/AutoRest.sln.DotSettings
@@ -3,4 +3,5 @@
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. See License.txt in the project root for license information.
-
\ No newline at end of file
+
+ False
\ No newline at end of file
diff --git a/src/core/AutoRest.Core/ClientModel/KnownFormat.cs b/src/core/AutoRest.Core/ClientModel/KnownFormat.cs
new file mode 100644
index 0000000000000..c828c19bf8996
--- /dev/null
+++ b/src/core/AutoRest.Core/ClientModel/KnownFormat.cs
@@ -0,0 +1,32 @@
+// 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;
+
+namespace AutoRest.Core.ClientModel
+{
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public enum KnownFormat
+ {
+ none,
+ unknown,
+
+ @char,
+ int32,
+ int64,
+ @float,
+ @double,
+ @byte,
+ binary,
+ date,
+ date_time,
+ password,
+ date_time_rfc1123,
+ duration,
+ uuid,
+ base64url,
+ @decimal,
+ unixtime
+ }
+}
\ No newline at end of file
diff --git a/src/core/AutoRest.Core/ClientModel/KnownFormatExtensions.cs b/src/core/AutoRest.Core/ClientModel/KnownFormatExtensions.cs
new file mode 100644
index 0000000000000..a4fc0d42fa06f
--- /dev/null
+++ b/src/core/AutoRest.Core/ClientModel/KnownFormatExtensions.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+
+using System;
+
+namespace AutoRest.Core.ClientModel
+{
+ public static class KnownFormatExtensions
+ {
+ public static KnownFormat Parse(string formatValue)
+ {
+ if (string.IsNullOrWhiteSpace(formatValue))
+ {
+ return KnownFormat.none;
+ }
+
+ KnownFormat result;
+ return Enum.TryParse(formatValue.Replace('-', '_'), true, out result) ? result : KnownFormat.unknown;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/core/AutoRest.Core/ClientModel/PrimaryType.cs b/src/core/AutoRest.Core/ClientModel/PrimaryType.cs
index 70d9de5cdc65f..c7025d56957fc 100644
--- a/src/core/AutoRest.Core/ClientModel/PrimaryType.cs
+++ b/src/core/AutoRest.Core/ClientModel/PrimaryType.cs
@@ -37,6 +37,12 @@ public PrimaryType(KnownPrimaryType type)
///
public string Format { get; set; }
+ ///
+ /// Returns the KnownFormat of the Format string (provided it matches a KnownFormat)
+ /// Otherwise, returns KnownFormat.none
+ ///
+ public KnownFormat KnownFormat => KnownFormatExtensions.Parse(Format);
+
///
/// Returns a string representation of the PrimaryType object.
///
diff --git a/src/core/AutoRest.Core/Logging/Logger.cs b/src/core/AutoRest.Core/Logging/Logger.cs
index c0e1e53a76e6e..def62f47459e2 100644
--- a/src/core/AutoRest.Core/Logging/Logger.cs
+++ b/src/core/AutoRest.Core/Logging/Logger.cs
@@ -34,7 +34,9 @@ static Logger()
/// Optional arguments to use if message includes formatting.
public static void LogInfo(string message, params object[] args)
{
+ lock( typeof( Logger ) ) {
Entries.Add(new LogEntry(LogEntrySeverity.Info, string.Format(CultureInfo.InvariantCulture, message, args)));
+ }
}
///
diff --git a/src/generator/AutoRest.CSharp.Unit.Tests/AutoDynamic.cs b/src/generator/AutoRest.CSharp.Unit.Tests/AutoDynamic.cs
new file mode 100644
index 0000000000000..17d4b103c3cc2
--- /dev/null
+++ b/src/generator/AutoRest.CSharp.Unit.Tests/AutoDynamic.cs
@@ -0,0 +1,258 @@
+// 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;
+using System.Diagnostics;
+using System.Dynamic;
+using System.Linq;
+using System.Reflection;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Serialization;
+
+namespace AutoRest.CSharp.Unit.Tests
+{
+ ///
+ /// This is a class that creates a dynamic wrapper around any object and can allow
+ /// deep inspection of anything (private or otherwise).
+ /// Handy for testing.
+ ///
+ public class AutoDynamic : DynamicObject
+ {
+ ///
+ /// Specify the flags for accessing members
+ ///
+ private static readonly BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance
+ | BindingFlags.Static | BindingFlags.Public
+ | BindingFlags.IgnoreCase;
+
+ ///
+ /// The object we are going to wrap
+ ///
+ private readonly object _wrapped;
+
+ ///
+ /// Create a simple private wrapper
+ ///
+ public AutoDynamic(object o)
+ {
+ _wrapped = o;
+ }
+
+ ///
+ /// Returns a JSON representation.
+ ///
+ /// a JSON string
+ public string ToJson()
+ {
+ return JsonConvert.SerializeObject(
+ _wrapped,
+ Formatting.Indented,
+ new JsonSerializerSettings
+ {
+ Converters = new JsonConverter[] {new StringEnumConverter()},
+ ContractResolver = new CamelCasePropertyNamesContractResolver(),
+ NullValueHandling = NullValueHandling.Ignore,
+ ObjectCreationHandling = ObjectCreationHandling.Reuse
+ });
+ }
+
+ private bool CheckResult(object result, out object outresult)
+ {
+ if (result == null || result.GetType().GetTypeInfo().IsPrimitive
+ || result.GetType().GetTypeInfo().IsValueType || result is string)
+ {
+ outresult = result;
+ }
+ else
+ {
+ outresult = result.ToDynamic();
+ }
+
+ return true;
+ }
+
+ ///
+ /// Try invoking a method
+ ///
+ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
+ {
+ if (_wrapped == null)
+ {
+ result = null;
+ return true;
+ }
+ var types = args.Select(a => a != null ? a.GetType() : typeof(object));
+
+ var method = _wrapped.GetType().GetMethod(binder.Name, types.ToArray())
+ ?? _wrapped.GetType().GetMethod(binder.Name, flags);
+
+ if (method == null)
+ {
+ return base.TryInvokeMember(binder, args, out result);
+ }
+
+ return CheckResult(method.Invoke(_wrapped, args), out result);
+ }
+
+ public override bool TryConvert(ConvertBinder binder, out object result)
+ {
+ if (_wrapped == null)
+ {
+ result = null;
+ return false;
+ }
+
+ if (binder.ReturnType.IsInstanceOfType(_wrapped))
+ {
+ result = _wrapped;
+ return true;
+ }
+ return base.TryConvert(binder, out result);
+ }
+
+ public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
+ {
+ if (_wrapped == null)
+ {
+ result = null;
+ return false;
+ }
+
+ if (indexes.Length == 1 && indexes[0] is int)
+ {
+ var index = (int) indexes[0];
+ try
+ {
+ var arr = _wrapped as Array;
+ if (arr != null)
+ {
+ return CheckResult(arr.GetValue(index), out result);
+ }
+ }
+ catch
+ {
+ // nope...
+ }
+ }
+
+ // is it asking for a property as a field
+ foreach (
+ var prop in _wrapped.GetType().GetProperties(flags).Where(each => each.GetIndexParameters().Any()))
+ {
+ try
+ {
+ result = prop.GetValue(_wrapped, indexes);
+ return true;
+ }
+ catch (TargetParameterCountException)
+ {
+ }
+ catch (TargetInvocationException)
+ {
+ }
+ }
+
+ if (indexes.Length == 1 && indexes[0] is int)
+ {
+ var index = (int) indexes[0];
+ try
+ {
+ var ie = _wrapped as IEnumerable;
+ if (ie != null)
+ {
+ var e = ie.GetEnumerator();
+
+ while (index > 0 && e.MoveNext())
+ {
+ --index;
+ }
+ if (index == 0)
+ {
+ if (e.MoveNext())
+ {
+ return CheckResult(e.Current, out result);
+ }
+ }
+ }
+ }
+ catch
+ {
+ }
+ }
+ return base.TryGetIndex(binder, indexes, out result);
+ }
+
+ ///
+ /// Tries to get a property or field with the given name
+ ///
+ public override bool TryGetMember(GetMemberBinder binder, out object result)
+ {
+ if (_wrapped == null)
+ {
+ result = null;
+ return true;
+ }
+
+ try
+ {
+ //Try getting a property of that name
+ var prop = _wrapped.GetType().GetProperty(binder.Name, flags);
+
+ if (prop == null)
+ {
+ //Try getting a field of that name
+ var fld = _wrapped.GetType().GetField(binder.Name, flags);
+
+ if (fld != null)
+ {
+ return CheckResult(fld.GetValue(_wrapped), out result);
+ }
+
+ // check if this is an index into the
+ if (TryGetIndex(null, new[] {binder.Name}, out result))
+ {
+ return true;
+ }
+
+ return base.TryGetMember(binder, out result);
+ }
+ return CheckResult(prop.GetValue(_wrapped, null), out result);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine($"{e.Message}/{e.StackTrace}");
+ result = null;
+ return true;
+ }
+ }
+
+ ///
+ /// Tries to set a property or field with the given name
+ ///
+ public override bool TrySetMember(SetMemberBinder binder, object value)
+ {
+ if (_wrapped == null)
+ {
+ return false;
+ }
+
+ var prop = _wrapped.GetType().GetProperty(binder.Name, flags);
+ if (prop == null)
+ {
+ var fld = _wrapped.GetType().GetField(binder.Name, flags);
+ if (fld != null)
+ {
+ fld.SetValue(_wrapped, value);
+ return true;
+ }
+ return base.TrySetMember(binder, value);
+ }
+
+ prop.SetValue(_wrapped, value, null);
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generator/AutoRest.CSharp.Unit.Tests/Bug1125.cs b/src/generator/AutoRest.CSharp.Unit.Tests/Bug1125.cs
new file mode 100644
index 0000000000000..c0c4fe4846886
--- /dev/null
+++ b/src/generator/AutoRest.CSharp.Unit.Tests/Bug1125.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.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace AutoRest.CSharp.Unit.Tests
+{
+ public class Bug1125 : BugTest
+ {
+ public Bug1125(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ ///
+ /// https://github.com/Azure/autorest/issues/1125
+ /// Support format:'char' for single character strings.
+ ///
+ [Fact]
+ public async Task SupportCharFormatForString()
+ {
+ // simplified test pattern for unit testing aspects of code generation
+ using (var fileSystem = GenerateCodeForTestFromSpec())
+ {
+ // Expected Files
+ Assert.True(fileSystem.FileExists(@"GeneratedCode\Models\ResultObject.cs"));
+ Assert.True(fileSystem.FileExists(@"GeneratedCode\Models\ResultObjectWithDefault.cs"));
+ Assert.True(fileSystem.FileExists(@"GeneratedCode\Models\ResultObjectWithMinMax.cs"));
+ Assert.True(fileSystem.FileExists(@"GeneratedCode\Models\ResultObjectWithExclusiveMinMax.cs"));
+ Assert.True(fileSystem.FileExists(@"GeneratedCode\Models\ParamObject.cs"));
+ Assert.True(fileSystem.FileExists(@"GeneratedCode\Models\ParamObjectWithDefault.cs"));
+
+ var result = await Compile(fileSystem);
+
+ // filter the warnings
+ var warnings = result.Messages.Where(
+ each => each.Severity == DiagnosticSeverity.Warning
+ && !SuppressWarnings.Contains(each.Id)).ToArray();
+
+ // use this to dump the files to disk for examination
+ // fileSystem.SaveFilesToTemp("bug1125");
+
+ // filter the errors
+ var errors = result.Messages.Where(each => each.Severity == DiagnosticSeverity.Error).ToArray();
+
+ Write(warnings, fileSystem);
+ Write(errors, fileSystem);
+
+ // use this to write out all the messages, even hidden ones.
+ // Write(result.Messages, fileSystem);
+
+ // Don't proceed unless we have zero Warnings.
+ Assert.Empty(warnings);
+
+ // Don't proceed unless we have zero Errors.
+ Assert.Empty(errors);
+
+ // Should also succeed.
+ Assert.True(result.Succeeded);
+
+ // try to load the assembly
+ var asm = Assembly.Load(result.Output.GetBuffer());
+ Assert.NotNull(asm);
+
+ // verify that we have the class we expected
+ var resultObject = asm.ExportedTypes.FirstOrDefault(each => each.FullName == "Test.Models.ResultObject");
+ Assert.NotNull(resultObject);
+
+ // verify the property is generated
+ var property = resultObject.GetProperty("SingleLetter");
+ Assert.NotNull(property);
+
+ // verify the type is as expected.
+ Assert.Equal(property.PropertyType, typeof(char?));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generator/AutoRest.CSharp.Unit.Tests/BugTest.cs b/src/generator/AutoRest.CSharp.Unit.Tests/BugTest.cs
index b1815563e740a..3b2064b5d25c8 100644
--- a/src/generator/AutoRest.CSharp.Unit.Tests/BugTest.cs
+++ b/src/generator/AutoRest.CSharp.Unit.Tests/BugTest.cs
@@ -1,24 +1,119 @@
// 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.IO;
-using AutoRest.Core.Logging;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
using AutoRest.Core.Utilities;
+using Microsoft.CodeAnalysis;
+using Microsoft.Rest.CSharp.Compiler.Compilation;
+using Xunit.Abstractions;
+using OutputKind = Microsoft.Rest.CSharp.Compiler.Compilation.OutputKind;
namespace AutoRest.CSharp.Unit.Tests
{
public class BugTest
{
+ private ITestOutputHelper _output;
+ internal static string[] SuppressWarnings = {"CS1701"};
+
+ public BugTest(ITestOutputHelper output)
+ {
+ _output = output;
+ }
+
public BugTest()
{
- Logger.Entries.Clear();
}
-
- protected MemoryFileSystem CreateMockFilesystem()
+
+ protected virtual MemoryFileSystem CreateMockFilesystem()
{
var fs = new MemoryFileSystem();
fs.Copy(Path.Combine("Resource", "AutoRest.json"));
return fs;
}
+
+ protected virtual MemoryFileSystem GenerateCodeForTestFromSpec()
+ {
+ var fs = CreateMockFilesystem();
+ $"{GetType().Name}.yaml".GenerateCodeInto(fs);
+ return fs;
+ }
+
+ protected virtual void WriteLine(object value)
+ {
+ if (value != null)
+ {
+ _output?.WriteLine(value.ToString());
+ Debug.WriteLine(value.ToString());
+ }
+ else
+ {
+ _output?.WriteLine("");
+ Debug.WriteLine("");
+ }
+ }
+
+ protected virtual void WriteLine(string format, params object[] values)
+ {
+ if (format != null)
+ {
+ if (values != null && values.Length > 0)
+ {
+ _output?.WriteLine(format, values);
+ Debug.WriteLine(format, values);
+ }
+ else
+ {
+ _output?.WriteLine(format);
+ Debug.WriteLine(format);
+ }
+ }
+ else
+ {
+ _output?.WriteLine("");
+ Debug.WriteLine("");
+ }
+ }
+
+ protected void Write(IEnumerable messages, MemoryFileSystem fileSystem)
+ {
+ if (messages.Any())
+ {
+ foreach (var file in messages.GroupBy(each => each.Location?.SourceTree?.FilePath, each => each))
+ {
+ var text = file.Key != null ? fileSystem.VirtualStore[file.Key].ToString() : string.Empty;
+
+ foreach (var error in file)
+ {
+ WriteLine(error.ToString());
+ // WriteLine(text.Substring(error.Location.SourceSpan.Start, error.Location.SourceSpan.Length));
+ }
+ }
+ }
+ }
+
+ protected async Task Compile(IFileSystem fileSystem)
+ {
+ var compiler = new CSharpCompiler(
+ fileSystem.GetFiles("GeneratedCode", "*.cs", SearchOption.AllDirectories)
+ .Select(each => new KeyValuePair(each, fileSystem.ReadFileAsText(each))).ToArray(),
+ ManagedAssets.FrameworkAssemblies.Concat(
+ AppDomain.CurrentDomain.GetAssemblies()
+ .Where(each => !each.IsDynamic)
+ .Select(each => each.Location)
+ .Concat(new[]
+ {
+ Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
+ "Microsoft.Rest.ClientRuntime.dll")
+ })
+ ));
+ return await compiler.Compile(OutputKind.DynamicallyLinkedLibrary);
+ }
}
}
\ No newline at end of file
diff --git a/src/generator/AutoRest.CSharp.Unit.Tests/Resource/Bug1125.yaml b/src/generator/AutoRest.CSharp.Unit.Tests/Resource/Bug1125.yaml
new file mode 100644
index 0000000000000..2bd46f07c1c15
--- /dev/null
+++ b/src/generator/AutoRest.CSharp.Unit.Tests/Resource/Bug1125.yaml
@@ -0,0 +1,200 @@
+swagger: '2.0'
+info:
+ version: 1.0.0
+ title: Simple API
+paths:
+ /operation:
+ get:
+ operationId: my_operation
+ responses:
+ 200:
+ description: OK
+ schema:
+ $ref: '#/definitions/ResultObject'
+
+ /operation2:
+ get:
+ operationId: my_operation2
+ responses:
+ 200:
+ description: OK
+ schema:
+ $ref: '#/definitions/ResultObjectWithDefault'
+
+ '/operation3/{letter}':
+ put:
+ operationId: my_operation3
+ parameters:
+ - name: letter
+ in: path
+ required: true
+ type: string
+ format: char
+ description: a single letter
+
+ responses:
+ 200:
+ description: OK
+ schema:
+ $ref: '#/definitions/ResultObject'
+
+ '/operation4int/{anint}':
+ put:
+ operationId: my_operation4withint
+ parameters:
+ - name: anint
+ in: path
+ required: false
+ type: integer
+ format: int32
+ description: a single int
+
+ responses:
+ 200:
+ description: OK
+ schema:
+ $ref: '#/definitions/ResultObject'
+
+
+ '/operation4/{letter}':
+ put:
+ operationId: my_operation4
+ parameters:
+ - name: letter
+ in: path
+ required: false
+ type: string
+ format: char
+ description: a single letter that is optional
+
+ responses:
+ 200:
+ description: OK
+ schema:
+ $ref: '#/definitions/ResultObject'
+
+ /operation5:
+ put:
+ operationId: my_operation5
+ parameters:
+ - name: someparams
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/ParamObject'
+
+ responses:
+ 200:
+ description: OK
+ schema:
+ $ref: '#/definitions/ResultObject'
+
+ /operation6:
+ put:
+ operationId: my_operation6
+ parameters:
+ - name: someparams
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/ParamObjectWithDefault'
+
+ responses:
+ 200:
+ description: OK
+ schema:
+ $ref: '#/definitions/ResultObject'
+
+ '/operation7/{letter}':
+ get:
+ operationId: my_operation7
+ parameters:
+ - name: letter
+ in: path
+ required: true
+ type: string
+ format: char
+ minimum: a
+ maximum: z
+ description: a single letter that is between a and z (inclusive)
+
+ responses:
+ 200:
+ description: OK
+ schema:
+ $ref: '#/definitions/ResultObjectWithMinMax'
+
+ '/operation8/{letter}':
+ get:
+ operationId: my_operation8
+ parameters:
+ - name: letter
+ in: path
+ required: true
+ type: string
+ format: char
+ minimum: a
+ maximum: z
+ exclusiveMinimum: true
+ exclusiveMaximum: true
+ description: a single letter that is between a and z (exclusive)
+
+ responses:
+ 200:
+ description: OK
+ schema:
+ $ref: '#/definitions/ResultObjectWithExclusiveMinMax'
+
+
+definitions:
+ ResultObject:
+ properties:
+ SingleLetter:
+ type: string
+ format: char
+ description: 'This should be a char.'
+
+ ResultObjectWithDefault:
+ properties:
+ SingleLetterWithDefault:
+ type: string
+ format: char
+ default: x
+ description: 'This should be a char and the default should be x.'
+
+ ResultObjectWithMinMax:
+ properties:
+ SingleLetterWithDefault:
+ type: string
+ format: char
+ minimum: a
+ maximum: z
+ description: 'This should be a char and the default should be between a and z (inclusive).'
+
+ ResultObjectWithExclusiveMinMax:
+ properties:
+ SingleLetterWithDefault:
+ type: string
+ format: char
+ exclusiveMinimum: true
+ exclusiveMaximum: true
+ minimum: a
+ maximum: z
+ description: 'This should be a char and the default should be between a and z. (exclusive)'
+
+ ParamObject:
+ properties:
+ SingleLetter:
+ type: string
+ format: char
+ default: x
+ description: 'This should be a char'
+
+ ParamObjectWithDefault:
+ properties:
+ SingleLetterWithDefault:
+ type: string
+ format: char
+ default: x
+ description: 'This should be a char and the default should be x.'
+
diff --git a/src/generator/AutoRest.CSharp.Unit.Tests/TestExtensions.cs b/src/generator/AutoRest.CSharp.Unit.Tests/TestExtensions.cs
index 430e48b3e1368..8392343664d3f 100644
--- a/src/generator/AutoRest.CSharp.Unit.Tests/TestExtensions.cs
+++ b/src/generator/AutoRest.CSharp.Unit.Tests/TestExtensions.cs
@@ -1,6 +1,8 @@
// 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 AutoRest.Core;
using AutoRest.Core.Extensibility;
@@ -10,16 +12,32 @@ namespace AutoRest.CSharp.Unit.Tests
{
internal static class TestExtensions
{
- internal static void Copy(this IFileSystem fileSystem , string sourceFileOnDisk)
+ public static dynamic ToDynamic(this object anonymousObject)
{
- Copy( fileSystem, sourceFileOnDisk, Path.GetFileName(sourceFileOnDisk));
+ return new AutoDynamic(anonymousObject);
}
+
+ public static bool IsAnonymousType(this Type type)
+ {
+ var name = type.Name;
+ if (name.Length < 3)
+ {
+ return false;
+ }
+ return name[0] == '<' && name[1] == '>' && name.IndexOf("AnonymousType", StringComparison.Ordinal) > 0;
+ }
+
+ internal static void Copy(this IFileSystem fileSystem, string sourceFileOnDisk)
+ {
+ Copy(fileSystem, sourceFileOnDisk, Path.GetFileName(sourceFileOnDisk));
+ }
+
internal static void Copy(this IFileSystem fileSystem, string sourceFileOnDisk, string destination)
{
fileSystem.WriteFile(destination, File.ReadAllText(sourceFileOnDisk));
}
- internal static MemoryFileSystem GenerateCodeInto(this string inputFile, MemoryFileSystem fileSystem )
+ internal static MemoryFileSystem GenerateCodeInto(this string inputFile, MemoryFileSystem fileSystem)
{
fileSystem.Copy(Path.Combine("Resource", inputFile));
@@ -29,16 +47,42 @@ internal static MemoryFileSystem GenerateCodeInto(this string inputFile, MemoryF
CodeGenerator = "CSharp",
FileSystem = fileSystem,
OutputDirectory = "GeneratedCode",
- Input = inputFile,
+ Namespace = "Test",
+ Input = inputFile
};
var codeGenerator = new CSharpCodeGenerator(settings);
- Modeler modeler = ExtensionsLoader.GetModeler(settings);
+ var modeler = ExtensionsLoader.GetModeler(settings);
var sc = modeler.Build();
codeGenerator.NormalizeClientModel(sc);
codeGenerator.Generate(sc).GetAwaiter().GetResult();
return fileSystem;
}
+
+ internal static void SaveFilesToTemp(this IFileSystem fileSystem, string folderName = null)
+ {
+ folderName = string.IsNullOrWhiteSpace(folderName) ? Guid.NewGuid().ToString() : folderName;
+ var outputFolder = Path.Combine(Path.GetTempPath(), folderName);
+ if (Directory.Exists(outputFolder))
+ {
+ try
+ {
+ Directory.Delete(outputFolder, true);
+ }
+ catch
+ {
+ // who cares...
+ }
+ }
+
+ Directory.CreateDirectory(outputFolder);
+ foreach (var file in fileSystem.GetFiles("", "*", SearchOption.AllDirectories))
+ {
+ var target = Path.Combine(outputFolder, file.Substring(file.IndexOf(":", StringComparison.Ordinal) + 1));
+ Directory.CreateDirectory(Path.GetDirectoryName(target));
+ File.WriteAllText(target, fileSystem.ReadFileAsText(file));
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/generator/AutoRest.CSharp.Unit.Tests/project.json b/src/generator/AutoRest.CSharp.Unit.Tests/project.json
index 347aab23721eb..a3068b920daff 100644
--- a/src/generator/AutoRest.CSharp.Unit.Tests/project.json
+++ b/src/generator/AutoRest.CSharp.Unit.Tests/project.json
@@ -14,7 +14,7 @@
"keyFile": "../../../Tools/MSSharedLibKey.snk",
"copyToOutput": {
- "include": ["Resource"],
+ "include": ["Resource"]
}
},
diff --git a/src/generator/AutoRest.CSharp/CSharpCodeNamer.cs b/src/generator/AutoRest.CSharp/CSharpCodeNamer.cs
index 7d4f9b313d98f..6ef0116ac0e37 100644
--- a/src/generator/AutoRest.CSharp/CSharpCodeNamer.cs
+++ b/src/generator/AutoRest.CSharp/CSharpCodeNamer.cs
@@ -234,78 +234,69 @@ public override IType NormalizeTypeDeclaration(IType type)
protected virtual IType NormalizePrimaryType(PrimaryType primaryType)
{
- if (primaryType == null)
- {
- return null;
- }
-
- if (primaryType.Type == KnownPrimaryType.Base64Url)
+ switch (primaryType?.Type)
{
+ case KnownPrimaryType.Base64Url:
primaryType.Name = "byte[]";
- }
- else if (primaryType.Type == KnownPrimaryType.Boolean)
- {
+ break;
+ case KnownPrimaryType.Boolean:
primaryType.Name = "bool";
- }
- else if (primaryType.Type == KnownPrimaryType.ByteArray)
- {
+ break;
+ case KnownPrimaryType.ByteArray:
primaryType.Name = "byte[]";
- }
- else if (primaryType.Type == KnownPrimaryType.Date)
- {
+ break;
+ case KnownPrimaryType.Date:
primaryType.Name = "DateTime";
- }
- else if (primaryType.Type == KnownPrimaryType.DateTime)
- {
+ break;
+ case KnownPrimaryType.DateTime:
primaryType.Name = UseDateTimeOffset ? "DateTimeOffset" : "DateTime";
- }
- else if (primaryType.Type == KnownPrimaryType.DateTimeRfc1123)
- {
+ break;
+ case KnownPrimaryType.DateTimeRfc1123:
primaryType.Name = "DateTime";
- }
- else if (primaryType.Type == KnownPrimaryType.Double)
- {
+ break;
+ case KnownPrimaryType.Double:
primaryType.Name = "double";
- }
- else if (primaryType.Type == KnownPrimaryType.Decimal)
- {
+ break;
+ case KnownPrimaryType.Decimal:
primaryType.Name = "decimal";
- }
- else if (primaryType.Type == KnownPrimaryType.Int)
- {
+ break;
+ case KnownPrimaryType.Int:
primaryType.Name = "int";
- }
- else if (primaryType.Type == KnownPrimaryType.Long)
- {
+ break;
+ case KnownPrimaryType.Long:
primaryType.Name = "long";
- }
- else if (primaryType.Type == KnownPrimaryType.Stream)
- {
+ break;
+ case KnownPrimaryType.Stream:
primaryType.Name = "System.IO.Stream";
- }
- else if (primaryType.Type == KnownPrimaryType.String)
+ break;
+ case KnownPrimaryType.String:
+ switch (KnownFormatExtensions.Parse( primaryType.Format ) )
{
+ case KnownFormat.@char:
+ primaryType.Name = "char";
+ break;
+
+ default:
primaryType.Name = "string";
+ break;
}
- else if (primaryType.Type == KnownPrimaryType.TimeSpan)
- {
+
+ break;
+ case KnownPrimaryType.TimeSpan:
primaryType.Name = "TimeSpan";
- }
- else if (primaryType.Type == KnownPrimaryType.Object)
- {
+ break;
+ case KnownPrimaryType.Object:
primaryType.Name = "object";
- }
- else if (primaryType.Type == KnownPrimaryType.Credentials)
- {
+ break;
+ case KnownPrimaryType.Credentials:
primaryType.Name = "ServiceClientCredentials";
- }
- else if (primaryType.Type == KnownPrimaryType.UnixTime)
- {
+ break;
+ case KnownPrimaryType.UnixTime:
primaryType.Name = "DateTime";
- }
- else if (primaryType.Type == KnownPrimaryType.Uuid)
- {
+ break;
+ case KnownPrimaryType.Uuid:
primaryType.Name = "Guid";
+ break;
}
return primaryType;
diff --git a/src/generator/AutoRest.CSharp/ClientModelExtensions.cs b/src/generator/AutoRest.CSharp/ClientModelExtensions.cs
index faf83b945610c..f3e4a4c0e8271 100644
--- a/src/generator/AutoRest.CSharp/ClientModelExtensions.cs
+++ b/src/generator/AutoRest.CSharp/ClientModelExtensions.cs
@@ -250,7 +250,7 @@ private static string GetSeparator(this CollectionFormat format)
public static string ToString(this IType type, string clientReference, string reference)
{
PrimaryType primaryType = type as PrimaryType;
- if (type == null || primaryType != null && primaryType.Type == KnownPrimaryType.String)
+ if (type == null || primaryType != null && primaryType.Type == KnownPrimaryType.String && primaryType.KnownFormat != KnownFormat.@char)
{
return reference;
}
@@ -303,21 +303,33 @@ public static bool CanBeNull(this IParameter parameter)
/// True if the type maps to a C# value type, otherwise false
public static bool IsValueType(this IType type)
{
- PrimaryType primaryType = type as PrimaryType;
- EnumType enumType = type as EnumType;
- return enumType != null ||
- (primaryType != null &&
- (primaryType.Type == KnownPrimaryType.Boolean
- || primaryType.Type == KnownPrimaryType.DateTime
- || primaryType.Type == KnownPrimaryType.Date
- || primaryType.Type == KnownPrimaryType.Decimal
- || primaryType.Type == KnownPrimaryType.Double
- || primaryType.Type == KnownPrimaryType.Int
- || primaryType.Type == KnownPrimaryType.Long
- || primaryType.Type == KnownPrimaryType.TimeSpan
- || primaryType.Type == KnownPrimaryType.DateTimeRfc1123
- || primaryType.Type == KnownPrimaryType.UnixTime
- || primaryType.Type == KnownPrimaryType.Uuid));
+ if (type is EnumType)
+ {
+ return true;
+ }
+
+ switch ((type as PrimaryType)?.Type )
+ {
+ case KnownPrimaryType.Boolean:
+ case KnownPrimaryType.DateTime:
+ case KnownPrimaryType.Date:
+ case KnownPrimaryType.Decimal:
+ case KnownPrimaryType.Double:
+ case KnownPrimaryType.Int:
+ case KnownPrimaryType.Long:
+ case KnownPrimaryType.TimeSpan:
+ case KnownPrimaryType.DateTimeRfc1123:
+ case KnownPrimaryType.UnixTime:
+ case KnownPrimaryType.Uuid:
+ return true;
+
+ case KnownPrimaryType.String:
+ return ((PrimaryType) type).KnownFormat == KnownFormat.@char;
+
+ default:
+ return false;
+ }
+
}
public static string CheckNull(string valueReference, string executionBlock)
@@ -359,7 +371,7 @@ public static string ValidateType(this IType type, IScopeProvider scope, string
if (constraints != null && constraints.Any())
{
- AppendConstraintValidations(valueReference, constraints, sb);
+ AppendConstraintValidations(valueReference, constraints, sb, (type as PrimaryType)?.KnownFormat ?? KnownFormat.none);
}
if (sequence != null && sequence.ShouldValidateChain())
@@ -403,60 +415,49 @@ public static string ValidateType(this IType type, IScopeProvider scope, string
}
- private static void AppendConstraintValidations(string valueReference, Dictionary constraints, IndentedStringBuilder sb)
+ private static void AppendConstraintValidations(string valueReference, Dictionary constraints, IndentedStringBuilder sb, KnownFormat format)
{
foreach (var constraint in constraints.Keys)
{
string constraintCheck;
- string constraintValue = constraints[constraint];
+ string constraintValue = (format == KnownFormat.@char) ?$"'{constraints[constraint]}'" : constraints[constraint];
switch (constraint)
{
case Constraint.ExclusiveMaximum:
- constraintCheck = string.Format(CultureInfo.InvariantCulture, "{0} >= {1}", valueReference,
- constraints[constraint]);
+ constraintCheck = $"{valueReference} >= {constraintValue}";
break;
case Constraint.ExclusiveMinimum:
- constraintCheck = string.Format(CultureInfo.InvariantCulture, "{0} <= {1}", valueReference,
- constraints[constraint]);
+ constraintCheck = $"{valueReference} <= {constraintValue}";
break;
case Constraint.InclusiveMaximum:
- constraintCheck = string.Format(CultureInfo.InvariantCulture, "{0} > {1}", valueReference,
- constraints[constraint]);
+ constraintCheck = $"{valueReference} > {constraintValue}";
break;
case Constraint.InclusiveMinimum:
- constraintCheck = string.Format(CultureInfo.InvariantCulture, "{0} < {1}", valueReference,
- constraints[constraint]);
+ constraintCheck = $"{valueReference} < {constraintValue}";
break;
case Constraint.MaxItems:
- constraintCheck = string.Format(CultureInfo.InvariantCulture, "{0}.Count > {1}", valueReference,
- constraints[constraint]);
+ constraintCheck = $"{valueReference}.Count > {constraintValue}";
break;
case Constraint.MaxLength:
- constraintCheck = string.Format(CultureInfo.InvariantCulture, "{0}.Length > {1}", valueReference,
- constraints[constraint]);
+ constraintCheck = $"{valueReference}.Length > {constraintValue}";
break;
case Constraint.MinItems:
- constraintCheck = string.Format(CultureInfo.InvariantCulture, "{0}.Count < {1}", valueReference,
- constraints[constraint]);
+ constraintCheck = $"{valueReference}.Count < {constraintValue}";
break;
case Constraint.MinLength:
- constraintCheck = string.Format(CultureInfo.InvariantCulture, "{0}.Length < {1}", valueReference,
- constraints[constraint]);
+ constraintCheck = $"{valueReference}.Length < {constraintValue}";
break;
case Constraint.MultipleOf:
- constraintCheck = string.Format(CultureInfo.InvariantCulture, "{0} % {1} != 0", valueReference,
- constraints[constraint]);
+ constraintCheck = $"{valueReference} % {constraintValue} != 0";
break;
case Constraint.Pattern:
- constraintValue = "\"" + constraintValue.Replace("\\", "\\\\") + "\"";
- constraintCheck = string.Format(CultureInfo.InvariantCulture,
- "!System.Text.RegularExpressions.Regex.IsMatch({0}, {1})", valueReference, constraintValue);
+ constraintValue = $"\"{constraintValue.Replace("\\", "\\\\")}\"";
+ constraintCheck = $"!System.Text.RegularExpressions.Regex.IsMatch({valueReference}, {constraintValue})";
break;
case Constraint.UniqueItems:
if ("true".Equals(constraints[constraint], StringComparison.OrdinalIgnoreCase))
{
- constraintCheck = string.Format(CultureInfo.InvariantCulture,
- "{0}.Count != {0}.Distinct().Count()", valueReference);
+ constraintCheck = $"{valueReference}.Count != {valueReference}.Distinct().Count()";
}
else
{
diff --git a/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs b/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs
index cae98fa92e720..d6a9fdce24ef4 100644
--- a/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs
+++ b/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs
@@ -34,6 +34,12 @@ public abstract class SwaggerObject : SwaggerBase
///
public virtual string Format { get; set; }
+ ///
+ /// Returns the KnownFormat of the Format string (provided it matches a KnownFormat)
+ /// Otherwise, returns KnownFormat.none
+ ///
+ public KnownFormat KnownFormat => KnownFormatExtensions.Parse(Format);
+
///
/// Describes the type of items in the array.
///
@@ -97,56 +103,66 @@ public ObjectBuilder GetBuilder(SwaggerModeler swaggerSpecBuilder)
return new ObjectBuilder(this, swaggerSpecBuilder);
}
+ ///
+ /// Returns the PrimaryType that the SwaggerObject maps to, given the Type and the KnownFormat.
+ ///
+ /// Note: Since a given language still may interpret the value of the Format after this,
+ /// it is possible the final implemented type may not be the type given here.
+ ///
+ /// This allows languages to not have a specific PrimaryType decided by the Modeler.
+ ///
+ /// For example, if the Type is DataType.String, and the KnownFormat is 'char' the C# generator
+ /// will end up creating a char type in the generated code, but other languages will still
+ /// use string.
+ ///
+ ///
+ /// The PrimaryType that best represents this object.
+ ///
public PrimaryType ToType()
{
switch (Type)
{
case DataType.String:
- if (string.Equals("date", Format, StringComparison.OrdinalIgnoreCase))
+ switch (KnownFormat)
{
+ case KnownFormat.date:
return new PrimaryType(KnownPrimaryType.Date);
- }
- if (string.Equals("date-time", Format, StringComparison.OrdinalIgnoreCase))
- {
+ case KnownFormat.date_time:
return new PrimaryType(KnownPrimaryType.DateTime);
- }
- if (string.Equals("date-time-rfc1123", Format, StringComparison.OrdinalIgnoreCase))
- {
+ case KnownFormat.date_time_rfc1123:
return new PrimaryType(KnownPrimaryType.DateTimeRfc1123);
- }
- if (string.Equals("byte", Format, StringComparison.OrdinalIgnoreCase))
- {
+ case KnownFormat.@byte:
return new PrimaryType(KnownPrimaryType.ByteArray);
- }
- if (string.Equals("duration", Format, StringComparison.OrdinalIgnoreCase))
- {
+ case KnownFormat.duration:
return new PrimaryType(KnownPrimaryType.TimeSpan);
- }
- if (string.Equals("uuid", Format, StringComparison.OrdinalIgnoreCase))
- {
+ case KnownFormat.uuid:
return new PrimaryType(KnownPrimaryType.Uuid);
- }
- if (string.Equals("base64url", Format, StringComparison.OrdinalIgnoreCase))
- {
+ case KnownFormat.base64url:
return new PrimaryType(KnownPrimaryType.Base64Url);
+ default:
+ return new PrimaryType(KnownPrimaryType.String);
}
- return new PrimaryType(KnownPrimaryType.String);
+
case DataType.Number:
- if (string.Equals("decimal", Format, StringComparison.OrdinalIgnoreCase))
+ switch (KnownFormat)
{
+ case KnownFormat.@decimal:
return new PrimaryType(KnownPrimaryType.Decimal);
+ default:
+ return new PrimaryType(KnownPrimaryType.Double);
}
- return new PrimaryType(KnownPrimaryType.Double);
+
case DataType.Integer:
- if (string.Equals("int64", Format, StringComparison.OrdinalIgnoreCase))
+ switch (KnownFormat)
{
+ case KnownFormat.int64:
return new PrimaryType(KnownPrimaryType.Long);
- }
- if (string.Equals("unixtime", Format, StringComparison.OrdinalIgnoreCase))
- {
+ case KnownFormat.unixtime:
return new PrimaryType(KnownPrimaryType.UnixTime);
+ default:
+ return new PrimaryType(KnownPrimaryType.Int);
}
- return new PrimaryType(KnownPrimaryType.Int);
+
case DataType.Boolean:
return new PrimaryType(KnownPrimaryType.Boolean);
case DataType.Object: