diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs deleted file mode 100644 index 3c7867c32..000000000 --- a/ObjectPrinting/ObjectPrinter.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ObjectPrinting -{ - public class ObjectPrinter - { - public static PrintingConfig For() - { - return new PrintingConfig(); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs deleted file mode 100644 index a9e082117..000000000 --- a/ObjectPrinting/PrintingConfig.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Linq; -using System.Text; - -namespace ObjectPrinting -{ - public class PrintingConfig - { - public string PrintToString(TOwner obj) - { - return PrintToString(obj, 0); - } - - private string PrintToString(object obj, int nestingLevel) - { - //TODO apply configurations - if (obj == null) - return "null" + Environment.NewLine; - - var finalTypes = new[] - { - typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan) - }; - if (finalTypes.Contains(obj.GetType())) - return obj + Environment.NewLine; - - var identation = new string('\t', nestingLevel + 1); - var sb = new StringBuilder(); - var type = obj.GetType(); - sb.AppendLine(type.Name); - foreach (var propertyInfo in type.GetProperties()) - { - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), - nestingLevel + 1)); - } - return sb.ToString(); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/IPropertyPrintingConfig.cs b/ObjectPrinting/Solved/IPropertyPrintingConfig.cs new file mode 100644 index 000000000..8753cb1cf --- /dev/null +++ b/ObjectPrinting/Solved/IPropertyPrintingConfig.cs @@ -0,0 +1,9 @@ +using System.Reflection; + +namespace ObjectPrinting.Solved; + +public interface IPropertyPrintingConfig +{ + public PrintingConfig ParentConfig { get; } + public MemberInfo? MemberInfo { get; } +} \ No newline at end of file diff --git a/ObjectPrinting/Solved/ObjectExtensions.cs b/ObjectPrinting/Solved/ObjectExtensions.cs index b0c94553c..3dbe326c4 100644 --- a/ObjectPrinting/Solved/ObjectExtensions.cs +++ b/ObjectPrinting/Solved/ObjectExtensions.cs @@ -1,10 +1,9 @@ -namespace ObjectPrinting.Solved +namespace ObjectPrinting.Solved; + +public static class ObjectExtensions { - public static class ObjectExtensions + public static string? PrintToString(this T obj) { - public static string PrintToString(this T obj) - { - return ObjectPrinter.For().PrintToString(obj); - } + return ObjectPrinter.For().PrintToString(obj); } } \ No newline at end of file diff --git a/ObjectPrinting/Solved/ObjectPrinter.cs b/ObjectPrinting/Solved/ObjectPrinter.cs index 540ee769c..d9e5aec43 100644 --- a/ObjectPrinting/Solved/ObjectPrinter.cs +++ b/ObjectPrinting/Solved/ObjectPrinter.cs @@ -1,10 +1,9 @@ -namespace ObjectPrinting.Solved +namespace ObjectPrinting.Solved; + +public class ObjectPrinter { - public class ObjectPrinter + public static PrintingConfig For() { - public static PrintingConfig For() - { - return new PrintingConfig(); - } + return new PrintingConfig(); } } \ No newline at end of file diff --git a/ObjectPrinting/Solved/Printer.cs b/ObjectPrinting/Solved/Printer.cs new file mode 100644 index 000000000..c37228a0f --- /dev/null +++ b/ObjectPrinting/Solved/Printer.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace ObjectPrinting.Solved; + +public class Printer(PrintingSettings settings) +{ + private readonly HashSet parsedObjects = []; + + public string PrintToString(object? obj, int nestingLevel, MemberInfo? member) + { + var sb = new StringBuilder(); + if (TryPrintNullOrExcluded(obj, nestingLevel, sb)) + return sb.ToString(); + + var type = obj!.GetType(); + + if (TryPrintSimpleType(obj, type, member, sb)) + return sb.ToString(); + + var isRemoveNeeded = false; + + if (!type.IsValueType && obj is not string) + { + if (!parsedObjects.Add(obj)) + return $"Cyclic reference at {type.Name}" + Environment.NewLine; + isRemoveNeeded = true; + } + + var indentation = new string('\t', nestingLevel + 1); + var result = string.Empty; + + if (obj is IDictionary dictionary) + result = ProcessDictionary(dictionary, type, indentation, nestingLevel); + else if (obj is IEnumerable enumerable and not string) + result = ProcessEnumerable(enumerable, type, indentation, nestingLevel); + else + result = PrintComplexType(obj, type, indentation, nestingLevel); + + if (isRemoveNeeded) + parsedObjects.Remove(obj); + + return result; + } + + private string PrintComplexType(object obj, Type type, string indentation, int nestingLevel) + { + var sb = new StringBuilder(); + sb.AppendLine(type.Name); + + var printableMembers = GetPrintableMembers(type); + foreach (var memberInfo in printableMembers) + { + var value = GetMemberValue(memberInfo, obj); + sb.Append($"{indentation}{memberInfo.Name} = {PrintToString(value, nestingLevel + 1, memberInfo)}"); + } + + return sb.ToString(); + } + + private bool TryPrintNullOrExcluded(object? obj, int nestingLevel, StringBuilder result) + { + if (obj is null) + { + result.Append("null" + Environment.NewLine); + return true; + } + + var type = obj.GetType(); + + if (settings.ExcludedTypes.Contains(type) && nestingLevel > 0) + return true; + + return false; + } + + private string ProcessDictionary(IDictionary dictionary, Type type, string indentation, int nestingLevel) + { + var result = new StringBuilder(); + result.AppendLine(type.Name); + foreach (DictionaryEntry entry in dictionary) + { + result.Append($"{indentation}Key = {PrintToString(entry.Key, nestingLevel + 1, null)}"); + result.Append($"{indentation}Value = {PrintToString(entry.Value, nestingLevel + 1, null)}"); + } + return result.ToString(); + } + + private string ProcessEnumerable(IEnumerable enumerable, Type type, string indentation, int nestingLevel) + { + var result = new StringBuilder(); + + result.AppendLine(type.Name); + var index = 0; + foreach (var item in enumerable) + { + result.Append($"{indentation}[{index}] = {PrintToString(item, nestingLevel + 1, null)}"); + index++; + } + + return result.ToString(); + } + + private bool TryPrintSimpleType(object obj, Type type, MemberInfo? memberInfo, StringBuilder result) + { + if (memberInfo != null && settings.MemberSerializers.TryGetValue(memberInfo, out var memberSerializer)) + { + result.Append(memberSerializer(obj) + Environment.NewLine); + return true; + } + + if (settings.TypeSerializers.TryGetValue(type, out var typeSerializer)) + { + result.Append(typeSerializer(obj) + Environment.NewLine); + return true; + } + + if (memberInfo != null && settings.MemberCultures.TryGetValue(memberInfo, out var memberCulture) && + obj is IFormattable formattable1) + { + result.Append(formattable1.ToString(null, memberCulture) + Environment.NewLine); + return true; + } + + if (settings.TypeCultures.TryGetValue(type, out var typeCulture) && obj is IFormattable formattable2) + { + result.Append(formattable2.ToString(null, typeCulture) + Environment.NewLine); + return true; + } + + if (settings.PrimitiveTypes.Contains(type) || type.IsEnum) + { + result.Append(obj + Environment.NewLine); + return true; + } + + return false; + } + + private List GetPrintableMembers(Type type) + { + var result = new List(); + var flags = BindingFlags.Instance | BindingFlags.Public; + foreach (var propertyInfo in type.GetProperties(flags)) + { + if (!propertyInfo.CanRead) + continue; + + if (IsMemberExcluded(propertyInfo, propertyInfo.PropertyType)) + continue; + + result.Add(propertyInfo); + } + + foreach (var fieldInfo in type.GetFields(flags)) + { + if (IsMemberExcluded(fieldInfo, fieldInfo.FieldType)) + continue; + + result.Add(fieldInfo); + } + + return result; + } + + private bool IsMemberExcluded(MemberInfo member, Type memberType) + { + return settings.ExcludedMembers.Contains(member) || settings.ExcludedTypes.Contains(memberType); + } + + private object? GetMemberValue(MemberInfo member, object obj) + { + return member switch + { + PropertyInfo p => p.GetValue(obj), + FieldInfo f => f.GetValue(obj), + _ => null + }; + } +} \ No newline at end of file diff --git a/ObjectPrinting/Solved/PrintingConfig.cs b/ObjectPrinting/Solved/PrintingConfig.cs index 0ec5aeb2b..57d0ac801 100644 --- a/ObjectPrinting/Solved/PrintingConfig.cs +++ b/ObjectPrinting/Solved/PrintingConfig.cs @@ -1,62 +1,80 @@ using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Linq.Expressions; +using System.Reflection; using System.Text; -namespace ObjectPrinting.Solved +namespace ObjectPrinting.Solved; + +public class PrintingConfig { - public class PrintingConfig - { - public PropertyPrintingConfig Printing() - { - return new PropertyPrintingConfig(this); - } - - public PropertyPrintingConfig Printing(Expression> memberSelector) - { - return new PropertyPrintingConfig(this); - } - - public PrintingConfig Excluding(Expression> memberSelector) - { - return this; - } - - internal PrintingConfig Excluding() - { - return this; - } - - public string PrintToString(TOwner obj) - { - return PrintToString(obj, 0); - } - - private string PrintToString(object obj, int nestingLevel) - { - //TODO apply configurations - if (obj == null) - return "null" + Environment.NewLine; - - var finalTypes = new[] - { - typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan) - }; - if (finalTypes.Contains(obj.GetType())) - return obj + Environment.NewLine; - - var identation = new string('\t', nestingLevel + 1); - var sb = new StringBuilder(); - var type = obj.GetType(); - sb.AppendLine(type.Name); - foreach (var propertyInfo in type.GetProperties()) - { - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), - nestingLevel + 1)); - } - return sb.ToString(); - } + internal PrintingSettings Settings = new(); + + public PropertyPrintingConfig Printing() + { + return new PropertyPrintingConfig(this, null); + } + + public PropertyPrintingConfig Printing(Expression> memberSelector) + { + return new PropertyPrintingConfig(this, GetMemberInfo(memberSelector)); + } + + public PrintingConfig Excluding(Expression> memberSelector) + { + var member = GetMemberInfo(memberSelector); + Settings.ExcludedMembers.Add(member); + return this; + } + + public PrintingConfig Excluding() + { + Settings.ExcludedTypes.Add(typeof(TPropType)); + return this; + } + + public string PrintToString(TOwner obj) + { + var printer = new Printer(Settings); + return printer.PrintToString(obj, 0, null); + } + + private MemberInfo GetMemberInfo(Expression> memberSelector) + { + if (memberSelector.Body is MemberExpression memberExpression) + return memberExpression.Member; + throw new ArgumentException(); + } + + internal void AddTypeSerializer(Func serialize) + { + Settings.TypeSerializers[typeof(TPropType)] = o => serialize((TPropType)o); + } + + internal void AddMemberSerializer(MemberInfo? member, Func serialize) + { + ArgumentNullException.ThrowIfNull(member); + Settings.MemberSerializers[member] = o => serialize((TPropType)o); + } + + internal void AddTypeCulture(CultureInfo culture) + { + Settings.TypeCultures[typeof(TPropType)] = culture; + } + + internal void AddMemberCulture(MemberInfo member, CultureInfo culture) + { + if (member == null) + throw new ArgumentNullException(nameof(member)); + + Settings.MemberCultures[member] = culture; + } + + internal void AddStringTrimming(MemberInfo? member, int maxLen) + { + AddMemberSerializer(member, s => s.Length <= maxLen ? s : s.Substring(0, maxLen)); } } \ No newline at end of file diff --git a/ObjectPrinting/Solved/PrintingSettings.cs b/ObjectPrinting/Solved/PrintingSettings.cs new file mode 100644 index 000000000..9bb5b3d69 --- /dev/null +++ b/ObjectPrinting/Solved/PrintingSettings.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; + +namespace ObjectPrinting.Solved; + +public class PrintingSettings +{ + public HashSet ExcludedTypes = []; + public HashSet ExcludedMembers = []; + public Dictionary> TypeSerializers = new(); + public Dictionary> MemberSerializers = new(); + public Dictionary TypeCultures = new(); + public Dictionary MemberCultures = new(); + + public Type[] PrimitiveTypes = + [ + typeof(int), typeof(double), typeof(float), typeof(string), + typeof(DateTime), typeof(TimeSpan), typeof(decimal), typeof(Guid) + ]; +} \ No newline at end of file diff --git a/ObjectPrinting/Solved/PropertyPrintingConfig.cs b/ObjectPrinting/Solved/PropertyPrintingConfig.cs index a509697d1..e527760e4 100644 --- a/ObjectPrinting/Solved/PropertyPrintingConfig.cs +++ b/ObjectPrinting/Solved/PropertyPrintingConfig.cs @@ -1,32 +1,21 @@ using System; using System.Globalization; +using System.Reflection; -namespace ObjectPrinting.Solved +namespace ObjectPrinting.Solved; + +public class PropertyPrintingConfig(PrintingConfig printingConfig, MemberInfo? memberInfo) + : IPropertyPrintingConfig { - public class PropertyPrintingConfig : IPropertyPrintingConfig + public PrintingConfig Using(Func print) { - private readonly PrintingConfig printingConfig; - - public PropertyPrintingConfig(PrintingConfig printingConfig) - { - this.printingConfig = printingConfig; - } - - public PrintingConfig Using(Func print) - { - return printingConfig; - } - - public PrintingConfig Using(CultureInfo culture) - { - return printingConfig; - } - - PrintingConfig IPropertyPrintingConfig.ParentConfig => printingConfig; + if (memberInfo == null) + printingConfig.AddTypeSerializer(print); + else + printingConfig.AddMemberSerializer(memberInfo, print); + return printingConfig; } - public interface IPropertyPrintingConfig - { - PrintingConfig ParentConfig { get; } - } + PrintingConfig IPropertyPrintingConfig.ParentConfig => printingConfig; + MemberInfo? IPropertyPrintingConfig.MemberInfo => memberInfo; } \ No newline at end of file diff --git a/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs index dd3922394..f08ecc1b8 100644 --- a/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs +++ b/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs @@ -1,18 +1,34 @@ using System; +using System.Globalization; -namespace ObjectPrinting.Solved +namespace ObjectPrinting.Solved; + +public static class PropertyPrintingConfigExtensions { - public static class PropertyPrintingConfigExtensions + public static string? PrintToString(this T obj, Func, PrintingConfig> config) + { + return config(ObjectPrinter.For()).PrintToString(obj); + } + + public static PrintingConfig TrimmedToLength(this PropertyPrintingConfig propConfig, int maxLen) { - public static string PrintToString(this T obj, Func, PrintingConfig> config) - { - return config(ObjectPrinter.For()).PrintToString(obj); - } + IPropertyPrintingConfig config = propConfig; + var parent = config.ParentConfig; + var memberInfo = config.MemberInfo; - public static PrintingConfig TrimmedToLength(this PropertyPrintingConfig propConfig, int maxLen) - { - return ((IPropertyPrintingConfig)propConfig).ParentConfig; - } + parent.AddStringTrimming(memberInfo, maxLen); + return parent; + } + + public static PrintingConfig Using(this IPropertyPrintingConfig config, + CultureInfo culture) + where TPropType : IFormattable + { + if (config.MemberInfo == null) + config.ParentConfig.AddTypeCulture(culture); + else + config.ParentConfig.AddMemberCulture(config.MemberInfo, culture); + return config.ParentConfig; } } \ No newline at end of file diff --git a/ObjectPrinting/Solved/Tests/Person.cs b/ObjectPrinting/Solved/Tests/Person.cs deleted file mode 100644 index 858ebbf8d..000000000 --- a/ObjectPrinting/Solved/Tests/Person.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ObjectPrinting.Solved.Tests -{ - public class Person - { - public Guid Id { get; set; } - public string Name { get; set; } - public double Height { get; set; } - public int Age { get; set; } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs deleted file mode 100644 index 4c8b2445c..000000000 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NUnit.Framework; - -namespace ObjectPrinting.Tests -{ - [TestFixture] - public class ObjectPrinterAcceptanceTests - { - [Test] - public void Demo() - { - var person = new Person { Name = "Alex", Age = 19 }; - - var printer = ObjectPrinter.For(); - //1. Исключить из сериализации свойства определенного типа - //2. Указать альтернативный способ сериализации для определенного типа - //3. Для числовых типов указать культуру - //4. Настроить сериализацию конкретного свойства - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - //6. Исключить из сериализации конкретного свойства - - string s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - //8. ...с конфигурированием - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting/Tests/Person.cs deleted file mode 100644 index f95559554..000000000 --- a/ObjectPrinting/Tests/Person.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ObjectPrinting.Tests -{ - public class Person - { - public Guid Id { get; set; } - public string Name { get; set; } - public double Height { get; set; } - public int Age { get; set; } - } -} \ No newline at end of file diff --git a/ObjectPrintingTests/CyclicReference.cs b/ObjectPrintingTests/CyclicReference.cs new file mode 100644 index 000000000..ccdc6c31b --- /dev/null +++ b/ObjectPrintingTests/CyclicReference.cs @@ -0,0 +1,7 @@ +namespace ObjectPrintingTests; + +public class CyclicReference +{ + public string Name { get; set; } + public CyclicReference Obj { get; set; } +} \ No newline at end of file diff --git a/ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs similarity index 87% rename from ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs rename to ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs index ac52d5ee5..a270a0876 100644 --- a/ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs @@ -20,18 +20,19 @@ public void Demo() //3. Для числовых типов указать культуру .Printing().Using(CultureInfo.InvariantCulture) //4. Настроить сериализацию конкретного свойства + .Printing(p => p.Name).Using(i => i.Replace("A", "a")) //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) .Printing(p => p.Name).TrimmedToLength(10) //6. Исключить из сериализации конкретного свойства .Excluding(p => p.Age); - string s1 = printer.PrintToString(person); + string? s1 = printer.PrintToString(person); //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - string s2 = person.PrintToString(); + string? s2 = person.PrintToString(); //8. ...с конфигурированием - string s3 = person.PrintToString(s => s.Excluding(p => p.Age)); + string? s3 = person.PrintToString(s => s.Excluding(p => p.Age)); Console.WriteLine(s1); Console.WriteLine(s2); Console.WriteLine(s3); diff --git a/ObjectPrintingTests/ObjectPrinterTests.cs b/ObjectPrintingTests/ObjectPrinterTests.cs new file mode 100644 index 000000000..9fa6700fa --- /dev/null +++ b/ObjectPrintingTests/ObjectPrinterTests.cs @@ -0,0 +1,421 @@ +using System.Globalization; +using System.Text; +using FluentAssertions; +using ObjectPrinting.Solved; +using ObjectPrinting.Solved.Tests; + +namespace ObjectPrintingTests; + +public class ObjectPrinterTests +{ + private Person person = new(); + private Guid guid = Guid.NewGuid(); + private string newLine = Environment.NewLine; + [SetUp] + public void SetUp() + { + person = new Person + { + Id = guid, + Name = "Alex", + Age = 19, + Birthday = new DateTime(1982, 06, 01), + FriendsBirthdays = [new DateTime(1991, 03, 12), new DateTime(1985, 09, 19)], + FriendsNames = ["John", "Amy", "Martin"], + Height = 190.5, + Money = 1000.1m, + Pets = new() { { "Asya", "Cat" }, { "Garry", "Fish" } }, + Parent = new Person + { + Name = "Jack" + } + }; + } + + [Test] + public void PrintToString_ShouldPrintExactly_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .PrintToString(person); + + var expected = new StringBuilder(); + expected.AppendLine("Person") + .AppendLine($"\tId = {person.Id}") + .AppendLine($"\tName = {person.Name}") + .AppendLine($"\tHeight = {person.Height}") + .AppendLine($"\tAge = {person.Age}") + .AppendLine($"\tMoney = {person.Money}") + .AppendLine("\tParent = Person") + .AppendLine($"\t\tId = {person.Parent.Id}"); // возможно стоит добавить больше, либо разнести их на разные тесты + + result.Should().Contain(expected.ToString()); + } + + [Test] + public void PrintToString_ShouldPrintPrimitiveProperties_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .PrintToString(person); + + result.Should().Contain("Name = Alex") + .And.Contain("Age = 19") + .And.Contain($"Id = {guid}") + .And.Contain("Height = 190,5") + .And.Contain("Money = 1000,1") + .And.Contain("Birthday = 01.06.1982 0:00:00"); + } + + [Test] + public void PrintToString_ShouldPrintArray_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .PrintToString(person); + + result.Should().Contain("FriendsBirthdays = DateTime[]") + .And.Contain("[0] = 12.03.1991 0:00:00") + .And.Contain("[1] = 19.09.1985 0:00:00"); + } + + [Test] + public void PrintToString_ShouldPrintList_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .PrintToString(person); + + result.Should().Contain("FriendsNames = List") + .And.Contain("[0] = John") + .And.Contain("[1] = Amy") + .And.Contain("[2] = Martin"); + } + + [Test] + public void PrintToString_ShouldPrintDictionary_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .PrintToString(person); + + result.Should().Contain("Pets = Dictionary") + .And.Contain("Key = Asya") + .And.Contain("Value = Cat") + .And.Contain("Key = Garry") + .And.Contain("Value = Fish"); + } + + [Test] + public void PrintToString_ShouldPrintInstance_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .PrintToString(person); + + result.Should().Contain("Parent = Person") + .And.Contain("Name = Jack"); + } + + [Test] + public void PrintToString_ShouldExcludeInstanceType_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Excluding() + .PrintToString(person); + + result.Should().NotContain("Parent = Person") + .And.NotContain("Name = Jack"); + } + + [Test] + public void PrintToString_ShouldExcludeInstanceMember_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Excluding(m => m.Parent) + .PrintToString(person); + + result.Should().NotContain("Parent = Person") + .And.NotContain("Name = Jack"); + } + + + [Test] + public void PrintToString_ShouldExcludePrimitiveType_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Excluding() + .PrintToString(person); + + result.Should().NotContain("Birthday = 01.06.1982 0:00:00"); + } + + [Test] + public void PrintToString_ShouldExcludePrimitiveMember_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Excluding(m => m.Name) + .PrintToString(person); + + result.Should().NotContain("Name = Alex"); + } + + [Test] + public void PrintToString_ShouldExcludeArrayType_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Excluding() + .PrintToString(person); + + result.Should().NotContain("FriendsBirthdays = DateTime[]") + .And.NotContain("[0] = 12.03.1991 0:00:00") + .And.NotContain("[1] = 19.09.1985 0:00:00"); + } + + [Test] + public void PrintToString_ShouldExcludeArrayMember_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Excluding(m => m.FriendsBirthdays) + .PrintToString(person); + + result.Should().NotContain("FriendsBirthdays = DateTime[]") + .And.NotContain("[0] = 12.03.1991 0:00:00") + .And.NotContain("[1] = 19.09.1985 0:00:00"); + } + + [Test] + public void PrintToString_ShouldExcludeListType_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Excluding>() + .PrintToString(person); + + result.Should().NotContain("FriendsNames = List") + .And.NotContain("[0] = John") + .And.NotContain("[1] = Amy") + .And.NotContain("[2] = Martin"); + } + + [Test] + public void PrintToString_ShouldExcludeListMember_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Excluding(m => m.FriendsNames) + .PrintToString(person); + + result.Should().NotContain("FriendsNames = List") + .And.NotContain("[0] = John") + .And.NotContain("[1] = Amy") + .And.NotContain("[2] = Martin"); + } + + [Test] + public void PrintToString_ShouldExcludeDictionaryType_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Excluding>() + .PrintToString(person); + + result.Should().NotContain("Pets = Dictionary") + .And.NotContain("Key = Asya") + .And.NotContain("Value = Cat") + .And.NotContain("Key = Garry") + .And.NotContain("Value = Fish"); + } + + [Test] + public void PrintToString_ShouldExcludeDictionaryMember_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Excluding(m => m.Pets) + .PrintToString(person); + + result.Should().NotContain("Pets = Dictionary") + .And.NotContain("Key = Asya") + .And.NotContain("Value = Cat") + .And.NotContain("Key = Garry") + .And.NotContain("Value = Fish"); + } + + [Test] + public void PrintToString_ShouldSerializeStringTypeAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing().Using(s => s.Replace("A", "a")) + .PrintToString(person); + + result.Should().Contain("Name = alex"); + } + + [Test] + public void PrintToString_ShouldSerializeDateTimeTypeAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing().Using(s => s.ToString(CultureInfo.InvariantCulture).Replace("1982", "1337")) + .PrintToString(person); + + result.Should().Contain("Birthday = 06/01/1337 00:00:00"); + } + + [Test] + public void PrintToString_ShouldSerializeDecimalTypeAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing().Using(s => s.ToString(CultureInfo.InvariantCulture).Replace("0", "9")) + .PrintToString(person); + + result.Should().Contain("Money = 1999.1"); + } + + [Test] + public void PrintToString_ShouldSerializeStringMemberAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing(m => m.Name).Using(s => s.Replace("A", "a")) + .PrintToString(person); + + result.Should().Contain("Name = alex"); + } + + [Test] + public void PrintToString_ShouldSerializeDateTimeMemberAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing(m => m.Birthday).Using(s => s.ToString(CultureInfo.InvariantCulture).Replace("1982", "1337")) + .PrintToString(person); + + result.Should().Contain("Birthday = 06/01/1337 00:00:00"); + } + + [Test] + public void PrintToString_ShouldSerializeDecimalMemberAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing(m => m.Money).Using(s => s.ToString(CultureInfo.InvariantCulture).Replace("0", "9")) + .PrintToString(person); + + result.Should().Contain("Money = 1999.1"); + } + + [Test] + public void PrintToString_ShouldSerializeArrayTypeAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing().Using(s => string.Join(" ", s).Replace("1", "2")) + .PrintToString(person); + + result.Should().Contain("FriendsBirthdays = 22.03.2992 0:00:00 29.09.2985 0:00:00"); + } + + [Test] + public void PrintToString_ShouldSerializeListTypeAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing>().Using(s => string.Join(" ", s).Replace("A", "a")) + .PrintToString(person); + + result.Should().Contain("FriendsNames = John amy Martin"); + } + + [Test] + public void PrintToString_ShouldSerializeDictionaryTypeAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing>().Using(s => string.Join(" ", s).Replace("1", "2")) + .PrintToString(person); + + result.Should().Contain("Pets = [Asya, Cat] [Garry, Fish]"); + } + + [Test] + public void PrintToString_ShouldSerializeArrayMemberAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing(m => m.FriendsBirthdays).Using(s => string.Join(" ", s).Replace("1", "2")) + .PrintToString(person); + + result.Should().Contain("FriendsBirthdays = 22.03.2992 0:00:00 29.09.2985 0:00:00"); + } + + [Test] + public void PrintToString_ShouldSerializeListMemberAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing(m => m.FriendsNames).Using(s => string.Join(" ", s).Replace("A", "a")) + .PrintToString(person); + + result.Should().Contain("FriendsNames = John amy Martin"); + } + + [Test] + public void PrintToString_ShouldSerializeDictionaryMemberAlternatively_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing(m => m.Pets).Using(s => string.Join(" ", s).Replace("1", "2")) + .PrintToString(person); + + result.Should().Contain("Pets = [Asya, Cat] [Garry, Fish]"); + } + + [Test] + public void PrintToString_ShouldSerializeDoubleWithCulture_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing(m => m.Height).Using(CultureInfo.InvariantCulture) + .PrintToString(person); + + result.Should().Contain("Height = 190.5"); + result.Should().Contain("Money = 1000,1"); + } + + [Test] + public void PrintToString_ShouldSerializeDecimalWithCulture_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing(m => m.Money).Using(CultureInfo.InvariantCulture) + .PrintToString(person); + + result.Should().Contain("Money = 1000.1"); + result.Should().Contain("Height = 190,5"); + } + + [Test] + public void PrintToString_ShouldSerializeStringWithTrimming_WhenAllPropertiesAreSet() + { + var result = ObjectPrinter.For() + .Printing(m => m.Name).TrimmedToLength(2) + .PrintToString(person); + + result.Should().NotContain("Name = Alex") + .And.Contain("Name = Al"); + } + + [Test] + public void PrintToString_ShouldNotSerializeCyclicReference_WhenCyclicReferenceIsPresent() + { + var a = new CyclicReference + { + Name = "Test" + }; + + var b = new CyclicReference + { + Name = "Test2" + }; + + a.Obj = b; + b.Obj = a; + + var result = ObjectPrinter.For() + .PrintToString(a); + + result.Should().Contain("Obj = Cyclic reference at CyclicReference"); + } + + [Test] + public void PrintToString_ShouldNotPrintCyclicReference_WhenTwoReferencesOnClassPresent() + { + var a = new TestDto { Id = 1 }; + var b = new TestModel { A = a, B = a }; + + var result = ObjectPrinter.For() + .PrintToString(b); + + result.Should().NotContain("Cyclic reference at TestDto"); + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrintingTests.csproj b/ObjectPrintingTests/ObjectPrintingTests.csproj new file mode 100644 index 000000000..d60bf920b --- /dev/null +++ b/ObjectPrintingTests/ObjectPrintingTests.csproj @@ -0,0 +1,28 @@ + + + + net9.0 + latest + enable + enable + false + + + + + + + + + + + + + + + + + + + + diff --git a/ObjectPrintingTests/Person.cs b/ObjectPrintingTests/Person.cs new file mode 100644 index 000000000..64e9253a1 --- /dev/null +++ b/ObjectPrintingTests/Person.cs @@ -0,0 +1,18 @@ +using System; + +namespace ObjectPrinting.Solved.Tests +{ + public class Person + { + public Guid Id { get; set; } + public string Name { get; set; } + public double Height { get; set; } + public int Age { get; set; } + public decimal Money { get; set; } + public Person Parent { get; set; } + public DateTime Birthday { get; set; } + public List FriendsNames { get; set; } + public DateTime[] FriendsBirthdays { get; set; } + public Dictionary Pets { get; set; } + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/TestDto.cs b/ObjectPrintingTests/TestDto.cs new file mode 100644 index 000000000..d4994b5a2 --- /dev/null +++ b/ObjectPrintingTests/TestDto.cs @@ -0,0 +1,6 @@ +namespace ObjectPrintingTests; + +public class TestDto +{ + public int Id { get; set; } +} \ No newline at end of file diff --git a/ObjectPrintingTests/TestModel.cs b/ObjectPrintingTests/TestModel.cs new file mode 100644 index 000000000..a29045eac --- /dev/null +++ b/ObjectPrintingTests/TestModel.cs @@ -0,0 +1,7 @@ +namespace ObjectPrintingTests; + +public class TestModel +{ + public TestDto A { get; set; } + public TestDto B { get; set; } +} \ No newline at end of file diff --git a/fluent-api.sln b/fluent-api.sln index 69c8db9ed..ef4c89315 100644 --- a/fluent-api.sln +++ b/fluent-api.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping.Tests", "Samp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectacle", "Samples\Spectacle\Spectacle.csproj", "{EFA9335C-411B-4597-B0B6-5438D1AE04C3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrintingTests", "ObjectPrintingTests\ObjectPrintingTests.csproj", "{AFC04253-34F3-4B03-B1AF-CF6B68757A8E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -35,6 +37,10 @@ Global {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Release|Any CPU.Build.0 = Release|Any CPU + {AFC04253-34F3-4B03-B1AF-CF6B68757A8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AFC04253-34F3-4B03-B1AF-CF6B68757A8E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AFC04253-34F3-4B03-B1AF-CF6B68757A8E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AFC04253-34F3-4B03-B1AF-CF6B68757A8E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/fluent-api.sln.DotSettings b/fluent-api.sln.DotSettings index 135b83ecb..53fe49b2f 100644 --- a/fluent-api.sln.DotSettings +++ b/fluent-api.sln.DotSettings @@ -1,6 +1,9 @@  <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> + True True True Imported 10.10.2016