diff --git a/exercises/book-store/BookStore.cs b/exercises/book-store/BookStore.cs index aad6c61a67..f38dd4be02 100644 --- a/exercises/book-store/BookStore.cs +++ b/exercises/book-store/BookStore.cs @@ -3,7 +3,7 @@ public static class BookStore { - public static double CalculateTotalCost(List books) + public static double Total(IEnumerable books) { throw new NotImplementedException("You need to implement this function."); } diff --git a/exercises/book-store/BookStoreTest.cs b/exercises/book-store/BookStoreTest.cs index 4faed66a6e..31d1e6190d 100644 --- a/exercises/book-store/BookStoreTest.cs +++ b/exercises/book-store/BookStoreTest.cs @@ -1,83 +1,97 @@ -using System.Collections.Generic; -using System.Linq; +// This file was auto-generated based on version 1.0.1 of the canonical data. + using Xunit; public class BookStoreTest { [Fact] - public void Basket_with_single_book() + public void Only_a_single_book() { - Assert.Equal(8, BookStore.CalculateTotalCost(MakeList(1))); + var input = new[] { 1 }; + Assert.Equal(8, BookStore.Total(input)); } [Fact(Skip = "Remove to run test")] - public void Basket_with_two_of_same_book() + public void Two_of_the_same_book() { - Assert.Equal(16, BookStore.CalculateTotalCost(MakeList(2, 2))); + var input = new[] { 2, 2 }; + Assert.Equal(16, BookStore.Total(input)); } [Fact(Skip = "Remove to run test")] public void Empty_basket() { - Assert.Equal(0, BookStore.CalculateTotalCost(MakeList())); + var input = new int[0]; + Assert.Equal(0, BookStore.Total(input)); } [Fact(Skip = "Remove to run test")] - public void Basket_with_two_different_books() + public void Two_different_books() { - Assert.Equal(15.2, BookStore.CalculateTotalCost(MakeList(1, 2))); + var input = new[] { 1, 2 }; + Assert.Equal(15.2, BookStore.Total(input)); } [Fact(Skip = "Remove to run test")] - public void Basket_with_three_different_books() + public void Three_different_books() { - Assert.Equal(21.6, BookStore.CalculateTotalCost(MakeList(1, 2, 3))); + var input = new[] { 1, 2, 3 }; + Assert.Equal(21.6, BookStore.Total(input)); } [Fact(Skip = "Remove to run test")] - public void Basket_with_four_different_books() + public void Four_different_books() { - Assert.Equal(25.6, BookStore.CalculateTotalCost(MakeList(1, 2, 3, 4))); + var input = new[] { 1, 2, 3, 4 }; + Assert.Equal(25.6, BookStore.Total(input)); } [Fact(Skip = "Remove to run test")] - public void Basket_with_five_different_books() + public void Five_different_books() { - Assert.Equal(30, BookStore.CalculateTotalCost(MakeList(1, 2, 3, 4, 5))); + var input = new[] { 1, 2, 3, 4, 5 }; + Assert.Equal(30, BookStore.Total(input)); } [Fact(Skip = "Remove to run test")] - public void Basket_with_eight_books() + public void Two_groups_of_four_is_cheaper_than_group_of_five_plus_group_of_three() { - Assert.Equal(51.20, BookStore.CalculateTotalCost(MakeList(1, 1, 2, 2, 3, 3, 4, 5))); + var input = new[] { 1, 1, 2, 2, 3, 3, 4, 5 }; + Assert.Equal(51.2, BookStore.Total(input)); } [Fact(Skip = "Remove to run test")] - public void Basket_with_nine_books() + public void Group_of_four_plus_group_of_two_is_cheaper_than_two_groups_of_three() { - Assert.Equal(55.60, BookStore.CalculateTotalCost(MakeList(1, 1, 2, 2, 3, 3, 4, 4, 5))); + var input = new[] { 1, 1, 2, 2, 3, 4 }; + Assert.Equal(40.8, BookStore.Total(input)); } [Fact(Skip = "Remove to run test")] - public void Basket_with_ten_books() + public void Two_each_of_first_4_books_and_1_copy_each_of_rest() { - Assert.Equal(60, BookStore.CalculateTotalCost(MakeList(1, 1, 2, 2, 3, 3, 4, 4, 5, 5))); + var input = new[] { 1, 1, 2, 2, 3, 3, 4, 4, 5 }; + Assert.Equal(55.6, BookStore.Total(input)); } [Fact(Skip = "Remove to run test")] - public void Basket_with_eleven_books() + public void Two_copies_of_each_book() { - Assert.Equal(68, BookStore.CalculateTotalCost(MakeList(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1))); + var input = new[] { 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 }; + Assert.Equal(60, BookStore.Total(input)); } [Fact(Skip = "Remove to run test")] - public void Basket_with_twelve_books() + public void Three_copies_of_first_book_and_2_each_of_remaining() { - Assert.Equal(75.20, BookStore.CalculateTotalCost(MakeList(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2))); + var input = new[] { 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1 }; + Assert.Equal(68, BookStore.Total(input)); } - private static List MakeList(params int[] values) + [Fact(Skip = "Remove to run test")] + public void Three_each_of_first_2_books_and_2_each_of_remaining_books() { - return values.ToList(); + var input = new[] { 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2 }; + Assert.Equal(75.2, BookStore.Total(input)); } -} +} \ No newline at end of file diff --git a/exercises/book-store/Example.cs b/exercises/book-store/Example.cs index 4428c459d5..0064b376a4 100644 --- a/exercises/book-store/Example.cs +++ b/exercises/book-store/Example.cs @@ -4,14 +4,14 @@ public static class BookStore { - public static double CalculateTotalCost(List books) + public static double Total(IEnumerable books) { - return CalculateTotalCost(books, 0); + return Total(books, 0); } - private static double CalculateTotalCost(List books, double priceSoFar) + private static double Total(IEnumerable books, double priceSoFar) { - if (books.Count == 0) + if (!books.Any()) { return priceSoFar; } @@ -33,7 +33,7 @@ private static double CalculateTotalCost(List books, double priceSoFar) remaining.Remove(item); } - var price = CalculateTotalCost(remaining.ToList(), priceSoFar + CostPerGroup(i)); + var price = Total(remaining.ToList(), priceSoFar + CostPerGroup(i)); minPrice = Math.Min(minPrice, price); } diff --git a/exercises/transpose/TransposeTest.cs b/exercises/transpose/TransposeTest.cs index 2f98d2cf4c..b7893ed7c9 100644 --- a/exercises/transpose/TransposeTest.cs +++ b/exercises/transpose/TransposeTest.cs @@ -15,8 +15,7 @@ public void Empty_string() [Fact(Skip = "Remove to run test")] public void Two_characters_in_a_row() { - var input = - "A1"; + var input = "A1"; var expected = "A\n" + "1"; @@ -29,8 +28,7 @@ public void Two_characters_in_a_column() var input = "A\n" + "1"; - var expected = - "A1"; + var expected = "A1"; Assert.Equal(expected, Transpose.String(input)); } @@ -50,8 +48,7 @@ public void Simple() [Fact(Skip = "Remove to run test")] public void Single_line() { - var input = - "Single line."; + var input = "Single line."; var expected = "S\n" + "i\n" + diff --git a/generators/Exercise.cs b/generators/Exercise.cs index 005db65245..2d40d8d4c2 100644 --- a/generators/Exercise.cs +++ b/generators/Exercise.cs @@ -9,12 +9,10 @@ protected Exercise() { Name = GetType().ToExerciseName(); CanonicalData = CanonicalDataParser.Parse(Name); - Configuration = new ExerciseConfiguration(); } public string Name { get; } public CanonicalData CanonicalData { get; } - public ExerciseConfiguration Configuration { get; } public void Generate() => TestClassFile.Write(this, Render()); diff --git a/generators/ExerciseConfiguration.cs b/generators/ExerciseConfiguration.cs deleted file mode 100644 index 4d280556ab..0000000000 --- a/generators/ExerciseConfiguration.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using Newtonsoft.Json.Linq; - -namespace Generators -{ - public class ExerciseConfiguration - { - public TestedMethodType TestedMethodType { get; set; } = TestedMethodType.Static; - public Type ExceptionType { get; set; } = typeof(ArgumentException); - public Func ThrowExceptionWhenExpectedValueEquals { get; set; } = x => x is JObject; - } -} \ No newline at end of file diff --git a/generators/Exercises/BeerSong.cs b/generators/Exercises/BeerSong.cs index 0ece079243..5d74fded99 100644 --- a/generators/Exercises/BeerSong.cs +++ b/generators/Exercises/BeerSong.cs @@ -1,13 +1,11 @@ -using Generators.Output; - -namespace Generators.Exercises +namespace Generators.Exercises { public class BeerSong : Exercise { public BeerSong() { foreach (var canonicalDataCase in CanonicalData.Cases) - canonicalDataCase.Expected = new MultiLineString(canonicalDataCase.Expected.ToString()); + canonicalDataCase.UseExpectedParameter = true; } } } \ No newline at end of file diff --git a/generators/Exercises/BookStore.cs b/generators/Exercises/BookStore.cs new file mode 100644 index 0000000000..c2d86cf2d7 --- /dev/null +++ b/generators/Exercises/BookStore.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json.Linq; + +namespace Generators.Exercises +{ + public class BookStore : Exercise + { + public BookStore() + { + foreach (var canonicalDataCase in CanonicalData.Cases) + { + canonicalDataCase.Input = ((JArray)canonicalDataCase.Properties["basket"]).Values(); + canonicalDataCase.UseInputParameters = true; + } + } + } +} \ No newline at end of file diff --git a/generators/Exercises/FoodChain.cs b/generators/Exercises/FoodChain.cs index 69743a18f5..7e26edfff6 100644 --- a/generators/Exercises/FoodChain.cs +++ b/generators/Exercises/FoodChain.cs @@ -1,5 +1,4 @@ -using Generators.Output; -using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Linq; namespace Generators.Exercises { @@ -8,7 +7,10 @@ public class FoodChain : Exercise public FoodChain() { foreach (var canonicalDataCase in CanonicalData.Cases) - canonicalDataCase.Expected = new MultiLineString((JArray)canonicalDataCase.Expected); + { + canonicalDataCase.Expected = string.Join("\n", (JArray)canonicalDataCase.Expected); + canonicalDataCase.UseExpectedParameter = true; + } } } } \ No newline at end of file diff --git a/generators/Exercises/NthPrime.cs b/generators/Exercises/NthPrime.cs index 8b80055aca..3ee92f000b 100644 --- a/generators/Exercises/NthPrime.cs +++ b/generators/Exercises/NthPrime.cs @@ -6,8 +6,8 @@ public class NthPrime : Exercise { public NthPrime() { - Configuration.ExceptionType = typeof(ArgumentOutOfRangeException); - Configuration.ThrowExceptionWhenExpectedValueEquals = x => x is bool; + foreach (var canonicalDataCase in CanonicalData.Cases) + canonicalDataCase.ExceptionThrown = canonicalDataCase.Expected is bool ? typeof(ArgumentOutOfRangeException) : null; } } } \ No newline at end of file diff --git a/generators/Exercises/PerfectNumbers.cs b/generators/Exercises/PerfectNumbers.cs index 13d511c421..3f077f86b2 100644 --- a/generators/Exercises/PerfectNumbers.cs +++ b/generators/Exercises/PerfectNumbers.cs @@ -1,4 +1,7 @@ using System; +using Generators.Output; +using Humanizer; +using Newtonsoft.Json.Linq; namespace Generators.Exercises { @@ -6,20 +9,13 @@ public class PerfectNumbers : Exercise { public PerfectNumbers() { - Configuration.ExceptionType = typeof(ArgumentOutOfRangeException); - foreach (var canonicalDataCase in CanonicalData.Cases) { + canonicalDataCase.ExceptionThrown = canonicalDataCase.Expected is JObject ? typeof(ArgumentOutOfRangeException) : null; + if (canonicalDataCase.Expected is string classificationType) - canonicalDataCase.Expected = Enum.Parse(typeof(Classification), classificationType, true); + canonicalDataCase.Expected = new UnescapedValue($"Classification.{classificationType.Transform(To.SentenceCase)}"); } } - - private enum Classification - { - Abundant, - Deficient, - Perfect, - } } } \ No newline at end of file diff --git a/generators/Exercises/RomanNumerals.cs b/generators/Exercises/RomanNumerals.cs index 770595495e..8e2f12d141 100644 --- a/generators/Exercises/RomanNumerals.cs +++ b/generators/Exercises/RomanNumerals.cs @@ -4,10 +4,9 @@ public class RomanNumerals : Exercise { public RomanNumerals() { - Configuration.TestedMethodType = TestedMethodType.Extension; - foreach (var canonicalDataCase in CanonicalData.Cases) { + canonicalDataCase.TestedMethodType = TestedMethodType.Extension; canonicalDataCase.Property = "ToRoman"; canonicalDataCase.Description = "Number_" + canonicalDataCase.Description; } diff --git a/generators/Exercises/Transpose.cs b/generators/Exercises/Transpose.cs index bfc2bb5b3e..7211a5e580 100644 --- a/generators/Exercises/Transpose.cs +++ b/generators/Exercises/Transpose.cs @@ -1,4 +1,3 @@ -using Generators.Output; using Newtonsoft.Json.Linq; namespace Generators.Exercises @@ -10,8 +9,10 @@ public Transpose() foreach (var canonicalDataCase in CanonicalData.Cases) { canonicalDataCase.Property = "String"; - canonicalDataCase.Input = new MultiLineString((JArray)canonicalDataCase.Input); - canonicalDataCase.Expected = new MultiLineString((JArray)canonicalDataCase.Expected); + canonicalDataCase.Input = string.Join("\n", (JArray)canonicalDataCase.Input); + canonicalDataCase.Expected = string.Join("\n", (JArray)canonicalDataCase.Expected); + canonicalDataCase.UseInputParameters = true; + canonicalDataCase.UseExpectedParameter = true; } } } diff --git a/generators/Exercises/Wordy.cs b/generators/Exercises/Wordy.cs index fc234ad7b5..0cfde4dda8 100644 --- a/generators/Exercises/Wordy.cs +++ b/generators/Exercises/Wordy.cs @@ -1,10 +1,13 @@ -namespace Generators.Exercises +using System; + +namespace Generators.Exercises { public class Wordy : Exercise { public Wordy() { - Configuration.ThrowExceptionWhenExpectedValueEquals = x => x is bool; + foreach (var canonicalDataCase in CanonicalData.Cases) + canonicalDataCase.ExceptionThrown = canonicalDataCase.Expected is bool ? typeof(ArgumentException) : null; } } } \ No newline at end of file diff --git a/generators/Input/CanonicalDataCase.cs b/generators/Input/CanonicalDataCase.cs index 6a103abbe5..1b8b67697c 100644 --- a/generators/Input/CanonicalDataCase.cs +++ b/generators/Input/CanonicalDataCase.cs @@ -1,4 +1,6 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; namespace Generators.Input @@ -16,5 +18,20 @@ public class CanonicalDataCase public object Input { get; set; } public object Expected { get; set; } + + [JsonIgnore] + public IDictionary Properties { get; set; } + + [JsonIgnore] + public bool UseInputParameters { get; set; } + + [JsonIgnore] + public bool UseExpectedParameter { get; set; } + + [JsonIgnore] + public TestedMethodType TestedMethodType { get; set; } = TestedMethodType.Static; + + [JsonIgnore] + public Type ExceptionThrown { get; set; } } } \ No newline at end of file diff --git a/generators/Input/CanonicalDataCaseJsonConverter.cs b/generators/Input/CanonicalDataCaseJsonConverter.cs index 3520a26ee9..859b56eb0a 100644 --- a/generators/Input/CanonicalDataCaseJsonConverter.cs +++ b/generators/Input/CanonicalDataCaseJsonConverter.cs @@ -19,6 +19,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist var canonicalDataCase = new CanonicalDataCase(); serializer.Populate(new JTokenReader(jToken), canonicalDataCase); + canonicalDataCase.Properties = jToken.ToObject>(); canonicalDataCase.Input = GetInputProperty(jToken); return canonicalDataCase; diff --git a/generators/Output/ExceptionTestMethodGenerator.cs b/generators/Output/ExceptionTestMethodGenerator.cs index 93df8aa784..87fb84868a 100644 --- a/generators/Output/ExceptionTestMethodGenerator.cs +++ b/generators/Output/ExceptionTestMethodGenerator.cs @@ -7,6 +7,6 @@ public class ExceptionTestMethodGenerator : TestMethodGenerator { protected override IEnumerable Body => Variables.Append($"Assert.Throws<{ExceptionType}>(() => {TestedMethodInvocation});"); - private string ExceptionType => Configuration.ExceptionType.FullName; + private string ExceptionType => CanonicalDataCase.ExceptionThrown.FullName; } } \ No newline at end of file diff --git a/generators/Output/MultiLineString.cs b/generators/Output/MultiLineString.cs deleted file mode 100644 index 77a1a557cb..0000000000 --- a/generators/Output/MultiLineString.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using Newtonsoft.Json.Linq; - -namespace Generators.Output -{ - public class MultiLineString : IEnumerable - { - private readonly IEnumerable _lines; - - public MultiLineString(string str) => _lines = str.Split('\n'); - public MultiLineString(JToken jToken) => _lines = jToken.Values(); - public MultiLineString(IEnumerable lines) => _lines = lines; - - public IEnumerator GetEnumerator() => _lines.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} \ No newline at end of file diff --git a/generators/Output/TestClassGenerator.cs b/generators/Output/TestClassGenerator.cs index 7c610260fd..508e69dd76 100644 --- a/generators/Output/TestClassGenerator.cs +++ b/generators/Output/TestClassGenerator.cs @@ -18,7 +18,7 @@ public static class TestClassGenerator private static TestMethod CreateTestMethod(CanonicalDataCase canonicalDataCase, Exercise exercise) { - if (exercise.Configuration.ThrowExceptionWhenExpectedValueEquals(canonicalDataCase.Expected)) + if (canonicalDataCase.ExceptionThrown != null) return ExceptionTestMethod.Create(canonicalDataCase, exercise); if (canonicalDataCase.Expected is bool) diff --git a/generators/Output/TestMethodGenerator.cs b/generators/Output/TestMethodGenerator.cs index 0d5b535316..bb3aaf628c 100644 --- a/generators/Output/TestMethodGenerator.cs +++ b/generators/Output/TestMethodGenerator.cs @@ -14,7 +14,6 @@ public TestMethod Create(CanonicalDataCase canonicalDataCase, Exercise exercise) { CanonicalDataCase = canonicalDataCase; CanonicalData = exercise.CanonicalData; - Configuration = exercise.Configuration; return new TestMethod { MethodName = TestMethodName, Body = Body }; } @@ -23,7 +22,6 @@ public TestMethod Create(CanonicalDataCase canonicalDataCase, Exercise exercise) protected CanonicalDataCase CanonicalDataCase { get; private set; } protected CanonicalData CanonicalData { get; private set; } - protected ExerciseConfiguration Configuration { get; private set; } protected string TestMethodName => CanonicalDataCase.Description.ToTestMethodName(); protected string TestedClassName => CanonicalData.Exercise.ToTestedClassName(); @@ -32,15 +30,29 @@ public TestMethod Create(CanonicalDataCase canonicalDataCase, Exercise exercise) protected object Input => ValueFormatter.Format(CanonicalDataCase.Input); protected object Expected => ValueFormatter.Format(CanonicalDataCase.Expected); + protected object InputParameters => UseVariablesForInput ? string.Join(", ", InputVariableNames) : Input; protected object ExpectedParameter => UseVariableForExpected ? ExpectedVariableName : Expected; - protected object InputParameter => UseVariableForInput ? InputVariableName : Input; - protected IEnumerable InputVariableDeclaration => ValueFormatter.FormatVariable(CanonicalDataCase.Input, InputVariableName); + protected IEnumerable InputVariablesDeclaration => ValueFormatter.FormatVariables(CanonicalDataCase.Input, InputVariableNames); protected IEnumerable ExpectedVariableDeclaration => ValueFormatter.FormatVariable(CanonicalDataCase.Expected, ExpectedVariableName); protected IEnumerable SutVariableDeclaration => new [] {$"var {SutVariableName} = new {TestedClassName}();" }; - protected bool UseVariableForInput => CanonicalDataCase.Input is MultiLineString; - protected bool UseVariableForExpected => CanonicalDataCase.Expected is MultiLineString; + protected bool UseVariablesForInput => CanonicalDataCase.UseInputParameters; + protected bool UseVariableForExpected => CanonicalDataCase.UseExpectedParameter; + + protected IEnumerable InputVariableNames + { + get + { + switch (CanonicalDataCase.Input) + { + case IDictionary dict: + return dict.Keys; + default: + return new [] { InputVariableName }; + } + } + } protected IEnumerable Variables { @@ -48,13 +60,13 @@ protected IEnumerable Variables { var lines = new List(); - if (UseVariableForInput) - lines.AddRange(InputVariableDeclaration); + if (UseVariablesForInput) + lines.AddRange(InputVariablesDeclaration); if (UseVariableForExpected) lines.AddRange(ExpectedVariableDeclaration); - if (Configuration.TestedMethodType == TestedMethodType.Instance) + if (CanonicalDataCase.TestedMethodType == TestedMethodType.Instance) lines.AddRange(SutVariableDeclaration); return lines; @@ -65,14 +77,14 @@ protected string TestedMethodInvocation { get { - switch (Configuration.TestedMethodType) + switch (CanonicalDataCase.TestedMethodType) { case TestedMethodType.Static: - return $"{TestedClassName}.{TestedMethodName}({InputParameter})"; + return $"{TestedClassName}.{TestedMethodName}({InputParameters})"; case TestedMethodType.Instance: - return $"{SutVariableName}.{TestedMethodName}({InputParameter})"; + return $"{SutVariableName}.{TestedMethodName}({InputParameters})"; case TestedMethodType.Extension: - return $"{InputParameter}.{TestedMethodName}()"; + return $"{InputParameters}.{TestedMethodName}()"; default: throw new ArgumentOutOfRangeException(); } diff --git a/generators/Output/UnescapedValue.cs b/generators/Output/UnescapedValue.cs new file mode 100644 index 0000000000..7d8680f03c --- /dev/null +++ b/generators/Output/UnescapedValue.cs @@ -0,0 +1,11 @@ +namespace Generators.Output +{ + public class UnescapedValue + { + private readonly string _value; + + public UnescapedValue(string value) => _value = value; + + public override string ToString() => _value; + } +} diff --git a/generators/Output/ValueFormatter.cs b/generators/Output/ValueFormatter.cs index 50d8279155..23312d4023 100644 --- a/generators/Output/ValueFormatter.cs +++ b/generators/Output/ValueFormatter.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; +using Newtonsoft.Json.Linq; namespace Generators.Output { @@ -13,42 +15,86 @@ public static object Format(object val) case IDictionary dict: return string.Join(", ", dict.Values.Select(Format)); case Enum enumeration: - return $"{enumeration.GetType().Name}.{enumeration}";; - case IEnumerable strings: - return strings.Select(FormatString); + return $"{enumeration.GetType().Name}.{enumeration}"; + case JArray jArray: + return $"new[] {{ {string.Join(", ", jArray.Select(Format))} }}"; case string str: return str.FormatString(); + case IEnumerable ints: + return ints.Any() ? $"new[] {{ {string.Join(", ", ints)} }}" : "new int[0]"; + case IEnumerable strings: + return strings.Any() ? $"new[] {{ {string.Join(", ", strings)} }}" : "new string[0]"; + case double dbl: + return dbl.ToString(CultureInfo.InvariantCulture); + case float flt: + return flt.ToString(CultureInfo.InvariantCulture); default: return val; } } - public static IEnumerable FormatVariable(object val, string name) + public static string[] FormatVariables(object val, IEnumerable names) { switch (val) { - case MultiLineString multiLineString: - if (!multiLineString.Any()) - return new [] {$"var {name} = {Format("")};"}; - - var lines = new List { $"var {name} = " }; - return lines.Concat( - multiLineString - .Select((t, i) => i < multiLineString.Count() - 1 - ? $" {Format(t + "\n")} +" - : $" {Format(t)};")); + case IDictionary dict: + return dict.Keys.SelectMany(key => FormatVariable(dict[key], key)).ToArray(); default: - return new[] {$"var expected = {Format(val)};"}; + return FormatVariable(val, names.First()); + } + } + + public static string[] FormatVariable(object val, string name) + { + switch (val) + { + case string str when str.Contains("\n"): + return FormatMultiLineString(name, str); + case int[] ints when ints.Any(): + return FormatMultiLineEnumerable(ints.Select(x => x.ToString(CultureInfo.InvariantCulture)), name); + case string[] strings when strings.Any(): + return FormatMultiLineEnumerable(strings, name); + default: + return new[] {$"var {name} = {Format(val)};"}; } } private static string FormatString(this string s) => s.EscapeControlCharacters().Quote(); - + + private static string[] FormatMultiLineString(string name, string str) + { + var strings = str.Split('\n'); + var formattedStrings = strings + .Select((t, i) => i < strings.Length - 1 + ? $"{Format(t + "\n")} +" + : $"{Format(t)}"); + + return FormatMultiLineVariable(formattedStrings, name); + } + + private static string[] FormatMultiLineEnumerable(IEnumerable enumerable, string name) + => FormatMultiLineVariable(enumerable.Prepend("{").Append("}"), name); + + private static string[] FormatMultiLineVariable(IEnumerable enumerable, string name) + => enumerable.Select(line => line.Indent()) + .AddTrailingSemicolon() + .Prepend($"var {name} = ") + .ToArray(); + private static string EscapeControlCharacters(this string s) => s.Replace("\n", "\\n") .Replace("\t", "\\t") .Replace("\r", "\\r"); private static string Quote(this string s) => $"\"{s}\""; + + private static string Indent(this string s, int level = 1) => $"{new string(' ', 4 * level)}{s}"; + + private static IEnumerable AddTrailingSemicolon(this IEnumerable enumerable) + { + var array = enumerable.ToArray(); + array[array.Length - 1] += ";"; + return array; + } } } \ No newline at end of file diff --git a/generators/Properties/launchSettings.json b/generators/Properties/launchSettings.json new file mode 100644 index 0000000000..ff0e07d91f --- /dev/null +++ b/generators/Properties/launchSettings.json @@ -0,0 +1,7 @@ +{ + "profiles": { + "Generators": { + "commandName": "Project" + } + } +} \ No newline at end of file