From b74451d0c8c299f22e8dcc9b3a50d370d358f48a Mon Sep 17 00:00:00 2001 From: maximka200 Date: Tue, 18 Nov 2025 15:19:30 +0500 Subject: [PATCH 01/13] =?UTF-8?q?-=20=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB?= =?UTF-8?q?=20=D0=BE=D1=81=D0=BD=D0=BE=D0=B2=D0=BD=D1=83=D1=8E=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80=D1=83=20-=20=D0=BD?= =?UTF-8?q?=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=BB=20=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=87=D0=B8=D0=B9=20PrintToString(c=20=D0=BE=D0=B3=D0=BE=D0=B2?= =?UTF-8?q?=D0=BE=D1=80=D0=BA=D0=B0=D0=BC=D0=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/ObjectPrinter.cs | 13 +- ObjectPrinting/PrintingConfig.cs | 115 ++++++++++++++---- ObjectPrinting/PropertyPrintingConfig.cs | 29 +++++ .../Tests/ObjectPrinterAcceptanceTests.cs | 29 +++-- ObjectPrinting/TypePrintingConfig.cs | 22 ++++ fluent-api.sln.DotSettings | 3 + 6 files changed, 166 insertions(+), 45 deletions(-) create mode 100644 ObjectPrinting/PropertyPrintingConfig.cs create mode 100644 ObjectPrinting/TypePrintingConfig.cs diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index 3c7867c32..c26c7a7c5 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -1,10 +1,9 @@ -namespace ObjectPrinting +namespace ObjectPrinting; + +public static 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/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a9e082117..43ddafb1a 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,41 +1,106 @@ using System; -using System.Linq; +using System.Collections.Generic; +using System.Linq.Expressions; using System.Text; -namespace ObjectPrinting +namespace ObjectPrinting; + +public class PrintingConfig { - public class PrintingConfig + internal Dictionary> TypeSerializers { get; } = new(); + internal Dictionary> PropertySerializers { get; } = new(); + private HashSet ExcludedTypes { get; } = new(); + private HashSet ExcludedProperties { get; } = new(); + internal Dictionary StringTrimmingRules { get; } = new(); + + public PrintingConfig Excluding() + { + ExcludedTypes.Add(typeof(TProp)); + return this; + } + + public PrintingConfig Excluding(Expression> selector) + { + ExcludedProperties.Add(GetPropertyName(selector)); + return this; + } + + public TypePrintingConfig Printing() + { + return new TypePrintingConfig(this); + } + + public PropertyPrintingConfig Printing( + Expression> selector) + { + return new PropertyPrintingConfig(this, selector); + } + + internal string GetPropertyName(Expression> selector) { - public string PrintToString(TOwner obj) + return selector.Body switch { - return PrintToString(obj, 0); + MemberExpression m => m.Member.Name, + UnaryExpression { Operand: MemberExpression m2 } => m2.Member.Name, + _ => throw new ArgumentException("Expression must be a property") + }; + } + + public string PrintToString(TOwner obj) + { + var sb = new StringBuilder(); + if (obj != null) Print(obj, sb, 0); + return sb.ToString(); + } + + private void Print(object obj, StringBuilder sb, int indent) + { + var type = obj.GetType(); + + if (ExcludedTypes.Contains(type)) + return; + + if (TypeSerializers.TryGetValue(type, out var typeSer)) + { + sb.Append(typeSer(obj)); + return; + } + + if (type.IsPrimitive || obj is string) + { + sb.Append(obj); + return; } - private string PrintToString(object obj, int nestingLevel) + sb.AppendLine(type.Name + ":"); + + foreach (var prop in type.GetProperties()) { - //TODO apply configurations - if (obj == null) - return "null" + Environment.NewLine; + var name = prop.Name; + + if (ExcludedProperties.Contains(name)) + continue; - var finalTypes = new[] + var value = prop.GetValue(obj); + + sb.Append(new string('\t', indent + 1)); + sb.Append(name + " = "); + + if (PropertySerializers.TryGetValue(name, out var propSer)) { - 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()) + if (value != null) sb.AppendLine(propSer(value)); + continue; + } + + if (value is string s && + StringTrimmingRules.TryGetValue(name, out var max)) { - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), - nestingLevel + 1)); + sb.AppendLine(s.Length <= max ? s : s.Substring(0, max)); + continue; } - return sb.ToString(); + + if (value != null) Print(value, sb, indent + 1); + sb.AppendLine(); } } } \ No newline at end of file diff --git a/ObjectPrinting/PropertyPrintingConfig.cs b/ObjectPrinting/PropertyPrintingConfig.cs new file mode 100644 index 000000000..439a4166c --- /dev/null +++ b/ObjectPrinting/PropertyPrintingConfig.cs @@ -0,0 +1,29 @@ +using System; +using System.Linq.Expressions; + +namespace ObjectPrinting; + +public class PropertyPrintingConfig( + PrintingConfig parent, + Expression> selector) +{ + private readonly string propertyName = parent.GetPropertyName(selector); + + public PrintingConfig Using(Func serializer) + { + parent.PropertySerializers[propertyName] = + x => serializer((TProp)x); + + return parent; + } + + public PrintingConfig TrimmedToLength(int maxLen) + { + if (typeof(TProp) != typeof(string)) + throw new InvalidOperationException( + "TrimmedToLength is only allowed for string properties"); + + parent.StringTrimmingRules[propertyName] = maxLen; + return parent; + } +} \ No newline at end of file diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs index 4c8b2445c..8f44bea69 100644 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs @@ -1,4 +1,6 @@ -using NUnit.Framework; +using System; +using System.Globalization; +using NUnit.Framework; namespace ObjectPrinting.Tests { @@ -8,20 +10,21 @@ 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. Исключить из сериализации конкретного свойства + var person = new Person {Id = new Guid(), Name = "Alex", Age = 19 }; + var printer = ObjectPrinter.For() + .Printing().Using(i => i.ToString("X")) + .Printing().Using(CultureInfo.InvariantCulture) + .Printing(p => p.Name).TrimmedToLength(3) + .Excluding(p => p.Age); + + var s1 = printer.PrintToString(person); + + //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию + var s2 = printer.PrintToString(person); - string s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию //8. ...с конфигурированием + + Console.WriteLine(s1); } } } \ No newline at end of file diff --git a/ObjectPrinting/TypePrintingConfig.cs b/ObjectPrinting/TypePrintingConfig.cs new file mode 100644 index 000000000..7bbcfe613 --- /dev/null +++ b/ObjectPrinting/TypePrintingConfig.cs @@ -0,0 +1,22 @@ +using System; + +namespace ObjectPrinting; + +public class TypePrintingConfig(PrintingConfig parent) +{ + public PrintingConfig ParentConfig { get; } = parent; + + public PrintingConfig Using(Func serializer) + { + ParentConfig.TypeSerializers[typeof(TProp)] = x => serializer((TProp)x); + return ParentConfig; + } + + public PrintingConfig Using(IFormatProvider provider) + { + ParentConfig.TypeSerializers[typeof(TProp)] = x => + Convert.ToString(x, provider); + + return ParentConfig; + } +} \ No newline at end of file diff --git a/fluent-api.sln.DotSettings b/fluent-api.sln.DotSettings index 135b83ecb..229f449d2 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" 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" Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> + True True True Imported 10.10.2016 From 07e7c2ef8a46305dd20ecd7cebb005b7da172e66 Mon Sep 17 00:00:00 2001 From: maximka200 Date: Wed, 19 Nov 2025 17:41:39 +0500 Subject: [PATCH 02/13] =?UTF-8?q?-=20=D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20-=20=D0=BD=D0=B0?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=B0=D0=BB=20=D1=80=D0=B0=D0=B1=D0=BE=D1=87?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=B2=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82(?= =?UTF-8?q?=D0=BD=D0=B5=20=D1=81=D0=B5=D1=80=D0=B8=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D1=83=D0=B5=D1=82=20=D0=BA=D0=BE=D0=BB=D0=BB=D0=B5=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D0=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/ObjectPrinter.cs | 6 + ObjectPrinting/ObjectPrinting.csproj | 1 + ObjectPrinting/PrintingConfig.cs | 32 +++- ObjectPrinting/PropertyPrintingConfig.cs | 2 +- .../Tests/ObjectPrinterAcceptanceTests.cs | 138 +++++++++++++++++- ObjectPrinting/Tests/Person.cs | 2 + ObjectPrinting/TypePrintingConfig.cs | 2 +- 7 files changed, 166 insertions(+), 17 deletions(-) diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index c26c7a7c5..bf7335edb 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -6,4 +6,10 @@ public static PrintingConfig For() { return new PrintingConfig(); } + + public static string PrintToString(object obj) + { + var printer = new PrintingConfig(); + return printer.PrintToString((TOwner)obj); + } } diff --git a/ObjectPrinting/ObjectPrinting.csproj b/ObjectPrinting/ObjectPrinting.csproj index c5db392ff..ea98111e3 100644 --- a/ObjectPrinting/ObjectPrinting.csproj +++ b/ObjectPrinting/ObjectPrinting.csproj @@ -5,6 +5,7 @@ + diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 43ddafb1a..97b8626d1 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -9,8 +9,10 @@ public class PrintingConfig { internal Dictionary> TypeSerializers { get; } = new(); internal Dictionary> PropertySerializers { get; } = new(); - private HashSet ExcludedTypes { get; } = new(); - private HashSet ExcludedProperties { get; } = new(); + private HashSet ExcludedTypes { get; } = []; + private HashSet ExcludedProperties { get; } = []; + + private readonly HashSet visited = []; internal Dictionary StringTrimmingRules { get; } = new(); public PrintingConfig Excluding() @@ -18,7 +20,6 @@ public PrintingConfig Excluding() ExcludedTypes.Add(typeof(TProp)); return this; } - public PrintingConfig Excluding(Expression> selector) { ExcludedProperties.Add(GetPropertyName(selector)); @@ -36,7 +37,7 @@ public PropertyPrintingConfig Printing( return new PropertyPrintingConfig(this, selector); } - internal string GetPropertyName(Expression> selector) + internal static string GetPropertyName(Expression> selector) { return selector.Body switch { @@ -57,6 +58,20 @@ private void Print(object obj, StringBuilder sb, int indent) { var type = obj.GetType(); + if (obj == null) + { + sb.Append("null"); + return; + } + + if (visited.Contains(obj)) + { + sb.Append(""); + return; + } + + visited.Add(obj); + if (ExcludedTypes.Contains(type)) return; @@ -66,7 +81,7 @@ private void Print(object obj, StringBuilder sb, int indent) return; } - if (type.IsPrimitive || obj is string) + if (type.IsPrimitive || obj is string || type.GetProperties().Length == 0) { sb.Append(obj); return; @@ -77,8 +92,9 @@ private void Print(object obj, StringBuilder sb, int indent) foreach (var prop in type.GetProperties()) { var name = prop.Name; - - if (ExcludedProperties.Contains(name)) + var propType = prop.PropertyType; + + if (ExcludedProperties.Contains(name) || ExcludedTypes.Contains(propType)) continue; var value = prop.GetValue(obj); @@ -95,7 +111,7 @@ private void Print(object obj, StringBuilder sb, int indent) if (value is string s && StringTrimmingRules.TryGetValue(name, out var max)) { - sb.AppendLine(s.Length <= max ? s : s.Substring(0, max)); + sb.AppendLine(s.Length <= max ? s : s[..max]); continue; } diff --git a/ObjectPrinting/PropertyPrintingConfig.cs b/ObjectPrinting/PropertyPrintingConfig.cs index 439a4166c..4b44f246e 100644 --- a/ObjectPrinting/PropertyPrintingConfig.cs +++ b/ObjectPrinting/PropertyPrintingConfig.cs @@ -7,7 +7,7 @@ public class PropertyPrintingConfig( PrintingConfig parent, Expression> selector) { - private readonly string propertyName = parent.GetPropertyName(selector); + private readonly string propertyName = PrintingConfig.GetPropertyName(selector); public PrintingConfig Using(Func serializer) { diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs index 8f44bea69..2bacbd9e7 100644 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using FluentAssertions; using NUnit.Framework; namespace ObjectPrinting.Tests @@ -10,21 +11,144 @@ public class ObjectPrinterAcceptanceTests [Test] public void Demo() { - var person = new Person {Id = new Guid(), Name = "Alex", Age = 19 }; + var person = new Person {Id = new Guid(), Name = "Alex", Age = 112, Height = 1.89, + Child = new Person {Id = new Guid(), Name = "Roman", Age = 5, Height = 1.0} + }; + var printer = ObjectPrinter.For() - .Printing().Using(i => i.ToString("X")) + .Printing().Using(x => (x - 1).ToString()) .Printing().Using(CultureInfo.InvariantCulture) .Printing(p => p.Name).TrimmedToLength(3) - .Excluding(p => p.Age); - - var s1 = printer.PrintToString(person); + .Excluding(p => p.Id) + .Excluding(); //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - var s2 = printer.PrintToString(person); - + var s2 = ObjectPrinter.PrintToString(person); //8. ...с конфигурированием + var s1 = printer.PrintToString(person); Console.WriteLine(s1); + Console.WriteLine("-----"); + Console.WriteLine(s2); + } + } + [TestFixture] + public class ObjectPrinterTests + { + private class A + { + public string Name { get; set; } + public int Number { get; set; } + public double Price { get; set; } + public Guid Id { get; set; } + } + + private class B + { + public A Data { get; set; } + public B Parent { get; set; } + } + + [Test] + public void Should_Exclude_Type_From_Serialization() + { + var obj = new A { Id = Guid.NewGuid(), Name = "Alex" }; + + var printer = ObjectPrinter.For() + .Excluding(); + + var result = printer.PrintToString(obj); + + result.Should().NotContain(nameof(A.Id)); + } + + [Test] + public void Should_Use_Custom_Serialization_For_Type() + { + var obj = new A { Number = 42 }; + + var printer = ObjectPrinter.For() + .Printing() + .Using(x => $"INT({x})"); + + var result = printer.PrintToString(obj); + + result.Should().Contain("INT(42)"); + } + + [Test] + public void Should_Apply_CultureInfo_To_Type() + { + var obj = new A { Price = 1234.56 }; + + var printer = ObjectPrinter.For() + .Printing() + .Using(CultureInfo.GetCultureInfo("fr-FR")); // 1234,56 + + var result = printer.PrintToString(obj); + + result.Should().Contain("1234,56"); + } + + [Test] + public void Should_Apply_Custom_Serialization_To_Specific_Property() + { + var obj = new A { Name = "Alex" }; + + var printer = ObjectPrinter.For() + .Printing(a => a.Name) + .Using(n => $"NAME={n}"); + + var result = printer.PrintToString(obj); + + result.Should().Contain("NAME=Alex"); + } + + [Test] + public void Should_Trim_String_Property() + { + var obj = new A { Name = "Alexander" }; + + var printer = ObjectPrinter.For() + .Printing(a => a.Name) + .TrimmedToLength(4); + + var result = printer.PrintToString(obj); + + result.Should().Contain("Alex"); + result.Should().NotContain("Alexander"); + } + + [Test] + public void Should_Exclude_Specific_Property() + { + var obj = new A { Name = "Alex", Number = 10 }; + + var printer = ObjectPrinter.For() + .Excluding(a => a.Number); + + var result = printer.PrintToString(obj); + + result.Should().NotContain(nameof(A.Number)); + } + + [Test] + public void Should_Handle_Cyclic_References() + { + var parent = new B(); + var child = new B { Parent = parent }; + parent.Data = new A { Name = "Child" }; + parent.Parent = child; + + var printer = ObjectPrinter.For(); + + Action act = () => printer.PrintToString(parent); + + act.Should().NotThrow(); + + var result = printer.PrintToString(parent); + + result.Should().Contain(""); } } } \ No newline at end of file diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting/Tests/Person.cs index f95559554..e4e0572d0 100644 --- a/ObjectPrinting/Tests/Person.cs +++ b/ObjectPrinting/Tests/Person.cs @@ -8,5 +8,7 @@ public class Person public string Name { get; set; } public double Height { get; set; } public int Age { get; set; } + + public Person Child { get; set; } } } \ No newline at end of file diff --git a/ObjectPrinting/TypePrintingConfig.cs b/ObjectPrinting/TypePrintingConfig.cs index 7bbcfe613..514d47df5 100644 --- a/ObjectPrinting/TypePrintingConfig.cs +++ b/ObjectPrinting/TypePrintingConfig.cs @@ -4,7 +4,7 @@ namespace ObjectPrinting; public class TypePrintingConfig(PrintingConfig parent) { - public PrintingConfig ParentConfig { get; } = parent; + private PrintingConfig ParentConfig { get; } = parent; public PrintingConfig Using(Func serializer) { From d387ab05ee6ac1f27de58cfa394047fb53c6771b Mon Sep 17 00:00:00 2001 From: maximka200 Date: Thu, 20 Nov 2025 11:52:48 +0500 Subject: [PATCH 03/13] =?UTF-8?q?-=20=D0=B4=D0=BE=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2=20-=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D1=82=D0=B8=D0=BD=D0=B3=20=D0=BA=D0=BE=D0=BB?= =?UTF-8?q?=D0=BB=D0=B5=D0=BA=D1=86=D0=B8=D0=B9(IEnumerable,=20IDictionary?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinterTests/CollectionPrintingTests.cs | 151 ++++++++++++++++++ .../ExpectedResults/Dictionary_Primitive.txt | 4 + .../Dictionary_With_ListValues.txt | 9 ++ .../ExpectedResults/IntArray.txt | 5 + .../ExpectedResults/List_Of_Lists.txt | 9 ++ .../Person_With_Collections.txt | 18 +++ .../ExpectedResults/StringList.txt | 4 + .../ExpectedResults/Team_With_Members.txt | 27 ++++ ObjectPrinterTests/Helper.cs | 17 ++ .../ObjectPrinterAcceptanceTests.cs | 43 ++--- ObjectPrinterTests/ObjectPrinterTests.csproj | 31 ++++ .../CollectionPrintingExtensions.cs | 55 +++++++ ObjectPrinting/ObjectExtensions.cs | 100 ++++++++++++ ObjectPrinting/PrintingConfig.cs | 82 ++-------- ObjectPrinting/Tests/Person.cs | 14 -- fluent-api.sln | 6 + 16 files changed, 467 insertions(+), 108 deletions(-) create mode 100644 ObjectPrinterTests/CollectionPrintingTests.cs create mode 100644 ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt create mode 100644 ObjectPrinterTests/ExpectedResults/Dictionary_With_ListValues.txt create mode 100644 ObjectPrinterTests/ExpectedResults/IntArray.txt create mode 100644 ObjectPrinterTests/ExpectedResults/List_Of_Lists.txt create mode 100644 ObjectPrinterTests/ExpectedResults/Person_With_Collections.txt create mode 100644 ObjectPrinterTests/ExpectedResults/StringList.txt create mode 100644 ObjectPrinterTests/ExpectedResults/Team_With_Members.txt create mode 100644 ObjectPrinterTests/Helper.cs rename {ObjectPrinting/Tests => ObjectPrinterTests}/ObjectPrinterAcceptanceTests.cs (82%) create mode 100644 ObjectPrinterTests/ObjectPrinterTests.csproj create mode 100644 ObjectPrinting/CollectionPrintingExtensions.cs create mode 100644 ObjectPrinting/ObjectExtensions.cs delete mode 100644 ObjectPrinting/Tests/Person.cs diff --git a/ObjectPrinterTests/CollectionPrintingTests.cs b/ObjectPrinterTests/CollectionPrintingTests.cs new file mode 100644 index 000000000..9ab0ed667 --- /dev/null +++ b/ObjectPrinterTests/CollectionPrintingTests.cs @@ -0,0 +1,151 @@ +using FluentAssertions; +using ObjectPrinting; + +namespace ObjectPrinterTests; + +[TestFixture] +public class CollectionPrintingSnapshotTests +{ + private class Address + { + public string City { get; set; } = ""; + } + + private class Person + { + public string Name { get; set; } = ""; + public int[] Scores { get; set; } = []; + public List Tags { get; set; } = []; + public Dictionary Addresses { get; set; } = new(); + } + + private class Team + { + public string Title { get; set; } = ""; + public List Members { get; set; } = new(); + } + + [Test] + public void IntArray_Snapshot() + { + var config = new PrintingConfig(); + var value = new[] { 1, 2, 3 }; + + var actual = config.PrintToString(value); + var expected = Helper.Read("IntArray.txt"); + + actual.Should().Be(expected); + } + + [Test] + public void StringList_Snapshot() + { + var config = new PrintingConfig>(); + var value = new List { "one", "two" }; + + var actual = config.PrintToString(value); + var expected = Helper.Read("StringList.txt"); + + actual.Should().Be(expected); + } + + [Test] + public void Dictionary_Primitive_Snapshot() + { + var config = new PrintingConfig>(); + var value = new Dictionary + { + ["a"] = 1, + ["b"] = 2 + }; + + var actual = config.PrintToString(value); + var expected = Helper.Read("Dictionary_Primitive.txt"); + + actual.Should().Be(expected); + } + + [Test] + public void PersonWithCollections_Snapshot() + { + var config = new PrintingConfig(); + var person = new Person + { + Name = "John", + Scores = [10, 20], + Tags = ["dev", "qa"], + Addresses = new Dictionary + { + ["home"] = new() { City = "Chelyabinsk" }, + ["work"] = new() { City = "Sverdlovsk" } + } + }; + + var actual = config.PrintToString(person); + var expected = Helper.Read("Person_With_Collections.txt"); + + actual.Should().Be(expected); + } + + [Test] + public void List_Of_Lists_Snapshot() + { + var config = new PrintingConfig>>(); + var value = new List> + { + new() { 1, 2 }, + new() { 3 } + }; + + var actual = config.PrintToString(value); + var expected = Helper.Read("List_Of_Lists.txt"); + + actual.Should().Be(expected); + } + + [Test] + public void DictionaryWithListValues_Snapshot() + { + var config = new PrintingConfig>>(); + var value = new Dictionary> + { + ["first"] = [1, 2], + ["second"] = [3] + }; + + var actual = config.PrintToString(value); + var expected = Helper.Read("Dictionary_With_ListValues.txt"); + + actual.Should().Be(expected); + } + + [Test] + public void TeamWithMembers_Snapshot() + { + var config = new PrintingConfig(); + var team = new Team + { + Title = "My_Command", + Members = + { + new Person + { + Name = "Max", + Scores = [1], + Tags = ["dev"] + }, + new Person + { + Name = "Kate", + Scores = [2, 3], + Tags = ["qa"] + } + } + }; + + var actual = config.PrintToString(team); + var expected = Helper.Read("Team_With_Members.txt"); + + actual.Should().Be(expected); + } +} diff --git a/ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt b/ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt new file mode 100644 index 000000000..078ead0dc --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt @@ -0,0 +1,4 @@ +Dictionary`2 { + [a] = 1 + [b] = 2 +} \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/Dictionary_With_ListValues.txt b/ObjectPrinterTests/ExpectedResults/Dictionary_With_ListValues.txt new file mode 100644 index 000000000..3be9434a9 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/Dictionary_With_ListValues.txt @@ -0,0 +1,9 @@ +Dictionary`2 { + [first] = List`1 [ + 1 + 2 + ] + [second] = List`1 [ + 3 + ] +} \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/IntArray.txt b/ObjectPrinterTests/ExpectedResults/IntArray.txt new file mode 100644 index 000000000..087272e3e --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/IntArray.txt @@ -0,0 +1,5 @@ +Int32[] [ + 1 + 2 + 3 +] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/List_Of_Lists.txt b/ObjectPrinterTests/ExpectedResults/List_Of_Lists.txt new file mode 100644 index 000000000..402f29ee8 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/List_Of_Lists.txt @@ -0,0 +1,9 @@ +List`1 [ + List`1 [ + 1 + 2 + ] + List`1 [ + 3 + ] +] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/Person_With_Collections.txt b/ObjectPrinterTests/ExpectedResults/Person_With_Collections.txt new file mode 100644 index 000000000..e3a30a028 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/Person_With_Collections.txt @@ -0,0 +1,18 @@ +Person: + Name = John + Scores = Int32[] [ + 10 + 20 + ] + Tags = List`1 [ + dev + qa + ] + Addresses = Dictionary`2 { + [home] = Address: + City = Chelyabinsk + + [work] = Address: + City = Sverdlovsk + + } diff --git a/ObjectPrinterTests/ExpectedResults/StringList.txt b/ObjectPrinterTests/ExpectedResults/StringList.txt new file mode 100644 index 000000000..d1e44cd06 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/StringList.txt @@ -0,0 +1,4 @@ +List`1 [ + one + two +] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/Team_With_Members.txt b/ObjectPrinterTests/ExpectedResults/Team_With_Members.txt new file mode 100644 index 000000000..8756429fb --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/Team_With_Members.txt @@ -0,0 +1,27 @@ +Team: + Title = My_Command + Members = List`1 [ + Person: + Name = Max + Scores = Int32[] [ + 1 + ] + Tags = List`1 [ + dev + ] + Addresses = Dictionary`2 { + } + + Person: + Name = Kate + Scores = Int32[] [ + 2 + 3 + ] + Tags = List`1 [ + qa + ] + Addresses = Dictionary`2 { + } + + ] diff --git a/ObjectPrinterTests/Helper.cs b/ObjectPrinterTests/Helper.cs new file mode 100644 index 000000000..2175daf72 --- /dev/null +++ b/ObjectPrinterTests/Helper.cs @@ -0,0 +1,17 @@ +namespace ObjectPrinterTests; + +public static class Helper +{ + public static string Read(string fileName) + { + var testDir = TestContext.CurrentContext.TestDirectory; + + var dir = Path.Combine(testDir, "ExpectedResults"); + + var path = Path.Combine(dir, fileName); + if (!File.Exists(path)) + Assert.Fail($"File not found: {path}"); + + return File.ReadAllText(path); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs similarity index 82% rename from ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs rename to ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs index 2bacbd9e7..1cf7006b6 100644 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs @@ -8,6 +8,16 @@ namespace ObjectPrinting.Tests [TestFixture] public class ObjectPrinterAcceptanceTests { + private class Person + { + public Guid Id { get; set; } + public string Name { get; set; } + public double Height { get; set; } + public int Age { get; set; } + + public Person Child { get; set; } + } + [Test] public void Demo() { @@ -50,10 +60,9 @@ private class B } [Test] - public void Should_Exclude_Type_From_Serialization() + public void ShouldExcludeType_FromSerialization() { var obj = new A { Id = Guid.NewGuid(), Name = "Alex" }; - var printer = ObjectPrinter.For() .Excluding(); @@ -63,10 +72,9 @@ public void Should_Exclude_Type_From_Serialization() } [Test] - public void Should_Use_Custom_Serialization_For_Type() + public void ShouldUseCustomSerialization_ForType() { var obj = new A { Number = 42 }; - var printer = ObjectPrinter.For() .Printing() .Using(x => $"INT({x})"); @@ -77,13 +85,12 @@ public void Should_Use_Custom_Serialization_For_Type() } [Test] - public void Should_Apply_CultureInfo_To_Type() + public void ShouldApplyCultureInfo_ToType() { var obj = new A { Price = 1234.56 }; - var printer = ObjectPrinter.For() .Printing() - .Using(CultureInfo.GetCultureInfo("fr-FR")); // 1234,56 + .Using(CultureInfo.GetCultureInfo("fr-FR")); var result = printer.PrintToString(obj); @@ -91,10 +98,9 @@ public void Should_Apply_CultureInfo_To_Type() } [Test] - public void Should_Apply_Custom_Serialization_To_Specific_Property() + public void ShouldApplyСustomSerialization_ToProperty() { var obj = new A { Name = "Alex" }; - var printer = ObjectPrinter.For() .Printing(a => a.Name) .Using(n => $"NAME={n}"); @@ -105,10 +111,9 @@ public void Should_Apply_Custom_Serialization_To_Specific_Property() } [Test] - public void Should_Trim_String_Property() + public void ShouldTrimString_Property() { var obj = new A { Name = "Alexander" }; - var printer = ObjectPrinter.For() .Printing(a => a.Name) .TrimmedToLength(4); @@ -120,34 +125,30 @@ public void Should_Trim_String_Property() } [Test] - public void Should_Exclude_Specific_Property() + public void ShouldExcludeSpecific_Property() { var obj = new A { Name = "Alex", Number = 10 }; - var printer = ObjectPrinter.For() .Excluding(a => a.Number); - + var result = printer.PrintToString(obj); result.Should().NotContain(nameof(A.Number)); } [Test] - public void Should_Handle_Cyclic_References() + public void ShouldHandleCyclicReferences_InProperty() { var parent = new B(); var child = new B { Parent = parent }; parent.Data = new A { Name = "Child" }; parent.Parent = child; - var printer = ObjectPrinter.For(); - Action act = () => printer.PrintToString(parent); - - act.Should().NotThrow(); - + var act = () => printer.PrintToString(parent); var result = printer.PrintToString(parent); - + + act.Should().NotThrow(); result.Should().Contain(""); } } diff --git a/ObjectPrinterTests/ObjectPrinterTests.csproj b/ObjectPrinterTests/ObjectPrinterTests.csproj new file mode 100644 index 000000000..dc435c70c --- /dev/null +++ b/ObjectPrinterTests/ObjectPrinterTests.csproj @@ -0,0 +1,31 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + + diff --git a/ObjectPrinting/CollectionPrintingExtensions.cs b/ObjectPrinting/CollectionPrintingExtensions.cs new file mode 100644 index 000000000..184af7794 --- /dev/null +++ b/ObjectPrinting/CollectionPrintingExtensions.cs @@ -0,0 +1,55 @@ +using System.Collections; +using System.Text; + +namespace ObjectPrinting; + +public static class CollectionPrintingExtensions +{ + public static void PrintDictionary(this IDictionary dict, PrintingConfig config, + StringBuilder sb, int indent) + { + sb.AppendLine(dict.GetType().Name + " {"); + foreach (DictionaryEntry entry in dict) + { + sb.Append(new string('\t', indent + 1)); + sb.Append('['); + entry.Key.Print(config, sb, indent + 1); + sb.Append("] = "); + + if (entry.Value is null) + { + sb.AppendLine("null"); + } + else + { + entry.Value.Print(config, sb, indent + 1); + sb.AppendLine(); + } + } + + sb.Append(new string('\t', indent)); + sb.Append('}'); + } + + public static void PrintEnumerable(this IEnumerable enumerable, PrintingConfig config, + StringBuilder sb, int indent) + { + sb.AppendLine(enumerable.GetType().Name + " ["); + foreach (var item in enumerable) + { + sb.Append(new string('\t', indent + 1)); + if (item is null) + { + sb.AppendLine("null"); + } + else + { + item.Print(config, sb, indent + 1); + sb.AppendLine(); + } + } + + sb.Append(new string('\t', indent)); + sb.Append(']'); + } +} \ No newline at end of file diff --git a/ObjectPrinting/ObjectExtensions.cs b/ObjectPrinting/ObjectExtensions.cs new file mode 100644 index 000000000..bed1304af --- /dev/null +++ b/ObjectPrinting/ObjectExtensions.cs @@ -0,0 +1,100 @@ +using System.Collections; +using System.Text; + +namespace ObjectPrinting; + +public static class ObjectExtensions +{ + public static void Print(this object? obj, PrintingConfig config, + StringBuilder sb, int indent) + { + if (obj is null) + { + sb.Append("null"); + return; + } + + if (!config.Visited.Add(obj)) + { + sb.Append(""); + return; + } + + var type = obj.GetType(); + + if (config.ExcludedTypes.Contains(type)) + return; + + if (config.TypeSerializers.TryGetValue(type, out var typeSer)) + { + sb.Append(typeSer(obj)); + return; + } + + if (obj is IDictionary dict) + { + dict.PrintDictionary(config, sb, indent); + return; + } + + if (obj is IEnumerable enumerable && obj is not string) + { + enumerable.PrintEnumerable(config, sb, indent); + return; + } + + if (type.IsPrimitive || obj is string || type.GetProperties().Length == 0) + { + sb.Append(obj); + return; + } + + PrintDefaultObject(obj, config, sb, indent); + } + + private static void PrintDefaultObject(object obj, PrintingConfig config, + StringBuilder sb, int indent) + { + var type = obj.GetType(); + + sb.AppendLine(type.Name + ":"); + + foreach (var prop in type.GetProperties()) + { + var name = prop.Name; + var propType = prop.PropertyType; + + if (config.ExcludedProperties.Contains(name) || + config.ExcludedTypes.Contains(propType)) + continue; + + var value = prop.GetValue(obj); + + sb.Append(new string('\t', indent + 1)); + sb.Append(name); + sb.Append(" = "); + + if (config.PropertySerializers.TryGetValue(name, out var propSer)) + { + sb.AppendLine(value != null ? propSer(value) : "null"); + continue; + } + + if (value is string s && + config.StringTrimmingRules.TryGetValue(name, out var max)) + { + sb.AppendLine(s.Length <= max ? s : s[..max]); + continue; + } + + if (value is null) + { + sb.AppendLine("null"); + continue; + } + + value.Print(config, sb, indent + 1); + sb.AppendLine(); + } + } +} diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 97b8626d1..d8e84a9e8 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -7,13 +7,13 @@ namespace ObjectPrinting; public class PrintingConfig { - internal Dictionary> TypeSerializers { get; } = new(); - internal Dictionary> PropertySerializers { get; } = new(); - private HashSet ExcludedTypes { get; } = []; - private HashSet ExcludedProperties { get; } = []; + public Dictionary> TypeSerializers { get; } = new(); + public Dictionary> PropertySerializers { get; } = new(); + public HashSet ExcludedTypes { get; } = []; + public HashSet ExcludedProperties { get; } = []; - private readonly HashSet visited = []; - internal Dictionary StringTrimmingRules { get; } = new(); + public readonly HashSet Visited = []; + public Dictionary StringTrimmingRules { get; } = new(); public PrintingConfig Excluding() { @@ -50,73 +50,9 @@ internal static string GetPropertyName(Expression> se public string PrintToString(TOwner obj) { var sb = new StringBuilder(); - if (obj != null) Print(obj, sb, 0); + Visited.Clear(); + if (obj != null) + obj.Print(this, sb, 0); return sb.ToString(); } - - private void Print(object obj, StringBuilder sb, int indent) - { - var type = obj.GetType(); - - if (obj == null) - { - sb.Append("null"); - return; - } - - if (visited.Contains(obj)) - { - sb.Append(""); - return; - } - - visited.Add(obj); - - if (ExcludedTypes.Contains(type)) - return; - - if (TypeSerializers.TryGetValue(type, out var typeSer)) - { - sb.Append(typeSer(obj)); - return; - } - - if (type.IsPrimitive || obj is string || type.GetProperties().Length == 0) - { - sb.Append(obj); - return; - } - - sb.AppendLine(type.Name + ":"); - - foreach (var prop in type.GetProperties()) - { - var name = prop.Name; - var propType = prop.PropertyType; - - if (ExcludedProperties.Contains(name) || ExcludedTypes.Contains(propType)) - continue; - - var value = prop.GetValue(obj); - - sb.Append(new string('\t', indent + 1)); - sb.Append(name + " = "); - - if (PropertySerializers.TryGetValue(name, out var propSer)) - { - if (value != null) sb.AppendLine(propSer(value)); - continue; - } - - if (value is string s && - StringTrimmingRules.TryGetValue(name, out var max)) - { - sb.AppendLine(s.Length <= max ? s : s[..max]); - continue; - } - - if (value != null) Print(value, sb, indent + 1); - sb.AppendLine(); - } - } } \ No newline at end of file diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting/Tests/Person.cs deleted file mode 100644 index e4e0572d0..000000000 --- a/ObjectPrinting/Tests/Person.cs +++ /dev/null @@ -1,14 +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; } - - public Person Child { get; set; } - } -} \ No newline at end of file diff --git a/fluent-api.sln b/fluent-api.sln index 69c8db9ed..79f860052 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}") = "ObjectPrinterTests", "ObjectPrinterTests\ObjectPrinterTests.csproj", "{4C8905B7-BFC8-4E4A-9DA1-B24435405856}" +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 + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From f526a1d8f4ccac7c4d61bb9ca70477f2b6f55ffe Mon Sep 17 00:00:00 2001 From: maximka200 Date: Tue, 25 Nov 2025 14:59:52 +0500 Subject: [PATCH 04/13] Stop tracking fluent-api.sln and ignore it --- ObjectPrinting/.gitignore | 0 fluent-api.sln | 53 --------------------------------------- 2 files changed, 53 deletions(-) create mode 100644 ObjectPrinting/.gitignore delete mode 100644 fluent-api.sln diff --git a/ObjectPrinting/.gitignore b/ObjectPrinting/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/fluent-api.sln b/fluent-api.sln deleted file mode 100644 index 79f860052..000000000 --- a/fluent-api.sln +++ /dev/null @@ -1,53 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrinting", "ObjectPrinting\ObjectPrinting.csproj", "{07B8C9B7-8289-46CB-9875-048A57758EEE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{6D308E4A-CEC7-4536-9B87-81CD337A87AD}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping", "Samples\FluentMapper\FluentMapping.csproj", "{FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping.Tests", "Samples\FluentMapper.Tests\FluentMapping.Tests.csproj", "{8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectacle", "Samples\Spectacle\Spectacle.csproj", "{EFA9335C-411B-4597-B0B6-5438D1AE04C3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrinterTests", "ObjectPrinterTests\ObjectPrinterTests.csproj", "{4C8905B7-BFC8-4E4A-9DA1-B24435405856}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {07B8C9B7-8289-46CB-9875-048A57758EEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {07B8C9B7-8289-46CB-9875-048A57758EEE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {07B8C9B7-8289-46CB-9875-048A57758EEE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {07B8C9B7-8289-46CB-9875-048A57758EEE}.Release|Any CPU.Build.0 = Release|Any CPU - {FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}.Release|Any CPU.Build.0 = Release|Any CPU - {8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}.Release|Any CPU.Build.0 = Release|Any CPU - {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {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 - {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E} = {6D308E4A-CEC7-4536-9B87-81CD337A87AD} - {8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D} = {6D308E4A-CEC7-4536-9B87-81CD337A87AD} - {EFA9335C-411B-4597-B0B6-5438D1AE04C3} = {6D308E4A-CEC7-4536-9B87-81CD337A87AD} - EndGlobalSection -EndGlobal From a8cce4f1508f4785b07d23ba52a24cb32465aca3 Mon Sep 17 00:00:00 2001 From: maximka200 Date: Tue, 25 Nov 2025 15:00:10 +0500 Subject: [PATCH 05/13] Stop tracking fluent-api.sln.DotSettings and ignore it --- fluent-api.sln.DotSettings | 86 -------------------------------------- 1 file changed, 86 deletions(-) delete mode 100644 fluent-api.sln.DotSettings diff --git a/fluent-api.sln.DotSettings b/fluent-api.sln.DotSettings deleted file mode 100644 index 229f449d2..000000000 --- a/fluent-api.sln.DotSettings +++ /dev/null @@ -1,86 +0,0 @@ - - <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" 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" Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> - True - True - True - Imported 10.10.2016 - NUnitTests - NUnit Test Fixture - True - getFileNameWithoutExtension() - 1 - True - 0 - True - True - 2.0 - InCSharpTypeAndNamespace - True - 2.0 - InCSharpTypeMember - tf - True - [NUnit.Framework.TestFixture] -public class $Class$_Should -{ - [NUnit.Framework.Test] - public void $DoSomething_WhenSomething$() - { - - } -} - True - True - Imported 10.10.2016 - NUnitTests - NUnit TearDown - True - True - 2.0 - InCSharpFile - td - True - [NUnit.Framework.TearDownAttribute] -public void TearDown() -{ - $END$ -} - - True - True - Imported 10.10.2016 - NUnitTests - NUnit Test - True - 0 - True - True - 2.0 - InCSharpTypeMember - tt - True - [NUnit.Framework.Test] -public void $DoSomething_WhenSomething$() -{ - $END$ -} - True - True - Imported 10.10.2016 - NUnitTests - NUnit SetUp - True - True - 2.0 - InCSharpFile - su - True - [NUnit.Framework.SetUpAttribute] -public void SetUp() -{ - $END$ -} - \ No newline at end of file From 1f9c262019f89cce2913601fc3f788a9e94d63b3 Mon Sep 17 00:00:00 2001 From: maximka200 Date: Tue, 25 Nov 2025 15:05:07 +0500 Subject: [PATCH 06/13] =?UTF-8?q?-=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=BB=20gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ObjectPrinting/.gitignore b/ObjectPrinting/.gitignore index e69de29bb..3520c9f9e 100644 --- a/ObjectPrinting/.gitignore +++ b/ObjectPrinting/.gitignore @@ -0,0 +1,2 @@ +fluent-api.sln +fluent-api.sln.DotSettings From 2b196256acaa6cfc083e925c0bbdf1b126ebca80 Mon Sep 17 00:00:00 2001 From: maximka200 Date: Tue, 25 Nov 2025 16:12:00 +0500 Subject: [PATCH 07/13] =?UTF-8?q?-=20=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=BF=D0=B0=D1=82=D1=82=D0=B5=D1=80?= =?UTF-8?q?=D0=BD=20=D1=81=D1=82=D1=80=D0=B0=D1=82=D0=B5=D0=B3=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20ObjectPrinter.cs=20-=20=D0=B2=D1=8B?= =?UTF-8?q?=D0=BD=D0=B5=D1=81=20=D0=B2=D1=81=D1=8E=20=D0=BB=D0=B8=D1=88?= =?UTF-8?q?=D0=BD=D1=8E=D1=8E=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D1=83=20?= =?UTF-8?q?=D0=B8=D0=B7=20PrintingConfig.cs=20=D0=B2=20PrintingConfigExten?= =?UTF-8?q?sions.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinterTests/CollectionPrintingTests.cs | 1 + .../ObjectPrinterAcceptanceTests.cs | 8 +- ObjectPrinting/.gitignore | 2 - .../{ => Configs}/PropertyPrintingConfig.cs | 5 +- .../{ => Configs}/TypePrintingConfig.cs | 2 +- .../CollectionPrintingExtensions.cs | 2 +- ObjectPrinting/Extensions/ObjectExtensions.cs | 43 ++++++++ .../Extensions/PrintingConfigExtensions.cs | 26 +++++ ObjectPrinting/IPrintStrategy.cs | 11 ++ ObjectPrinting/ObjectExtensions.cs | 100 ------------------ ObjectPrinting/ObjectPrinter.cs | 2 + ObjectPrinting/PrintingConfig.cs | 24 +---- ObjectPrinting/Solved/ObjectExtensions.cs | 10 -- ObjectPrinting/Solved/ObjectPrinter.cs | 10 -- ObjectPrinting/Solved/PrintingConfig.cs | 62 ----------- .../Solved/PropertyPrintingConfig.cs | 32 ------ .../PropertyPrintingConfigExtensions.cs | 18 ---- .../Tests/ObjectPrinterAcceptanceTests.cs | 40 ------- ObjectPrinting/Solved/Tests/Person.cs | 12 --- .../Strategies/DefaultObjectStrategy.cs | 55 ++++++++++ .../Strategies/DictionaryStrategy.cs | 18 ++++ .../Strategies/EnumerableStrategy.cs | 18 ++++ .../Strategies/ExcludedTypeStrategy.cs | 15 +++ .../Strategies/SimpleTypeStrategy.cs | 16 +++ .../Strategies/TypeSerializerStrategy.cs | 17 +++ 25 files changed, 234 insertions(+), 315 deletions(-) delete mode 100644 ObjectPrinting/.gitignore rename ObjectPrinting/{ => Configs}/PropertyPrintingConfig.cs (81%) rename ObjectPrinting/{ => Configs}/TypePrintingConfig.cs (94%) rename ObjectPrinting/{ => Extensions}/CollectionPrintingExtensions.cs (97%) create mode 100644 ObjectPrinting/Extensions/ObjectExtensions.cs create mode 100644 ObjectPrinting/Extensions/PrintingConfigExtensions.cs create mode 100644 ObjectPrinting/IPrintStrategy.cs delete mode 100644 ObjectPrinting/ObjectExtensions.cs delete mode 100644 ObjectPrinting/Solved/ObjectExtensions.cs delete mode 100644 ObjectPrinting/Solved/ObjectPrinter.cs delete mode 100644 ObjectPrinting/Solved/PrintingConfig.cs delete mode 100644 ObjectPrinting/Solved/PropertyPrintingConfig.cs delete mode 100644 ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs delete mode 100644 ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs delete mode 100644 ObjectPrinting/Solved/Tests/Person.cs create mode 100644 ObjectPrinting/Strategies/DefaultObjectStrategy.cs create mode 100644 ObjectPrinting/Strategies/DictionaryStrategy.cs create mode 100644 ObjectPrinting/Strategies/EnumerableStrategy.cs create mode 100644 ObjectPrinting/Strategies/ExcludedTypeStrategy.cs create mode 100644 ObjectPrinting/Strategies/SimpleTypeStrategy.cs create mode 100644 ObjectPrinting/Strategies/TypeSerializerStrategy.cs diff --git a/ObjectPrinterTests/CollectionPrintingTests.cs b/ObjectPrinterTests/CollectionPrintingTests.cs index 9ab0ed667..292283ec0 100644 --- a/ObjectPrinterTests/CollectionPrintingTests.cs +++ b/ObjectPrinterTests/CollectionPrintingTests.cs @@ -1,5 +1,6 @@ using FluentAssertions; using ObjectPrinting; +using ObjectPrinting.Extensions; namespace ObjectPrinterTests; diff --git a/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs index 1cf7006b6..86ebc2b0d 100644 --- a/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs @@ -1,9 +1,9 @@ -using System; -using System.Globalization; +using System.Globalization; using FluentAssertions; -using NUnit.Framework; +using ObjectPrinting; +using ObjectPrinting.Extensions; -namespace ObjectPrinting.Tests +namespace ObjectPrinterTests { [TestFixture] public class ObjectPrinterAcceptanceTests diff --git a/ObjectPrinting/.gitignore b/ObjectPrinting/.gitignore deleted file mode 100644 index 3520c9f9e..000000000 --- a/ObjectPrinting/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -fluent-api.sln -fluent-api.sln.DotSettings diff --git a/ObjectPrinting/PropertyPrintingConfig.cs b/ObjectPrinting/Configs/PropertyPrintingConfig.cs similarity index 81% rename from ObjectPrinting/PropertyPrintingConfig.cs rename to ObjectPrinting/Configs/PropertyPrintingConfig.cs index 4b44f246e..7823283a6 100644 --- a/ObjectPrinting/PropertyPrintingConfig.cs +++ b/ObjectPrinting/Configs/PropertyPrintingConfig.cs @@ -1,13 +1,14 @@ using System; using System.Linq.Expressions; +using ObjectPrinting.Extensions; -namespace ObjectPrinting; +namespace ObjectPrinting.Configs; public class PropertyPrintingConfig( PrintingConfig parent, Expression> selector) { - private readonly string propertyName = PrintingConfig.GetPropertyName(selector); + private readonly string propertyName = PrintingConfigExtensions.GetPropertyName(selector); public PrintingConfig Using(Func serializer) { diff --git a/ObjectPrinting/TypePrintingConfig.cs b/ObjectPrinting/Configs/TypePrintingConfig.cs similarity index 94% rename from ObjectPrinting/TypePrintingConfig.cs rename to ObjectPrinting/Configs/TypePrintingConfig.cs index 514d47df5..924a9bba3 100644 --- a/ObjectPrinting/TypePrintingConfig.cs +++ b/ObjectPrinting/Configs/TypePrintingConfig.cs @@ -1,6 +1,6 @@ using System; -namespace ObjectPrinting; +namespace ObjectPrinting.Configs; public class TypePrintingConfig(PrintingConfig parent) { diff --git a/ObjectPrinting/CollectionPrintingExtensions.cs b/ObjectPrinting/Extensions/CollectionPrintingExtensions.cs similarity index 97% rename from ObjectPrinting/CollectionPrintingExtensions.cs rename to ObjectPrinting/Extensions/CollectionPrintingExtensions.cs index 184af7794..edc550279 100644 --- a/ObjectPrinting/CollectionPrintingExtensions.cs +++ b/ObjectPrinting/Extensions/CollectionPrintingExtensions.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Text; -namespace ObjectPrinting; +namespace ObjectPrinting.Extensions; public static class CollectionPrintingExtensions { diff --git a/ObjectPrinting/Extensions/ObjectExtensions.cs b/ObjectPrinting/Extensions/ObjectExtensions.cs new file mode 100644 index 000000000..eba41132b --- /dev/null +++ b/ObjectPrinting/Extensions/ObjectExtensions.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ObjectPrinting.Strategies; + +namespace ObjectPrinting.Extensions; + +public static class ObjectExtensions +{ + private static readonly List Strategies = new() + { + new ExcludedTypeStrategy(), + new TypeSerializerStrategy(), + new DictionaryStrategy(), + new EnumerableStrategy(), + new SimpleTypeStrategy(), + new DefaultObjectStrategy() + }; + + public static void Print(this object? obj, PrintingConfig config, + StringBuilder sb, int indent) + { + if (obj is null) + { + sb.Append("null"); + return; + } + + if (!config.Visited.Add(obj)) + { + sb.Append(""); + return; + } + + var type = obj.GetType(); + + foreach (var strategy in Strategies.Where(strategy => strategy.CanPrint(obj, type, config))) + { + strategy.Print(obj, type, config, sb, indent); + return; + } + } +} \ No newline at end of file diff --git a/ObjectPrinting/Extensions/PrintingConfigExtensions.cs b/ObjectPrinting/Extensions/PrintingConfigExtensions.cs new file mode 100644 index 000000000..203991774 --- /dev/null +++ b/ObjectPrinting/Extensions/PrintingConfigExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Linq.Expressions; +using System.Text; + +namespace ObjectPrinting.Extensions; + +public static class PrintingConfigExtensions +{ + internal static string GetPropertyName(Expression> selector) + { + return selector.Body switch + { + MemberExpression m => m.Member.Name, + UnaryExpression { Operand: MemberExpression m2 } => m2.Member.Name, + _ => throw new ArgumentException("Expression must be a property") + }; + } + + public static string PrintToString(this PrintingConfig printingConfig, object? obj) + { + var sb = new StringBuilder(); + printingConfig.Visited.Clear(); + obj?.Print(printingConfig, sb, 0); + return sb.ToString(); + } +} \ No newline at end of file diff --git a/ObjectPrinting/IPrintStrategy.cs b/ObjectPrinting/IPrintStrategy.cs new file mode 100644 index 000000000..63c25d955 --- /dev/null +++ b/ObjectPrinting/IPrintStrategy.cs @@ -0,0 +1,11 @@ +using System; +using System.Text; + +namespace ObjectPrinting; + +public interface IPrintStrategy +{ + bool CanPrint(object obj, Type type, PrintingConfig config); + void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent); +} \ No newline at end of file diff --git a/ObjectPrinting/ObjectExtensions.cs b/ObjectPrinting/ObjectExtensions.cs deleted file mode 100644 index bed1304af..000000000 --- a/ObjectPrinting/ObjectExtensions.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.Collections; -using System.Text; - -namespace ObjectPrinting; - -public static class ObjectExtensions -{ - public static void Print(this object? obj, PrintingConfig config, - StringBuilder sb, int indent) - { - if (obj is null) - { - sb.Append("null"); - return; - } - - if (!config.Visited.Add(obj)) - { - sb.Append(""); - return; - } - - var type = obj.GetType(); - - if (config.ExcludedTypes.Contains(type)) - return; - - if (config.TypeSerializers.TryGetValue(type, out var typeSer)) - { - sb.Append(typeSer(obj)); - return; - } - - if (obj is IDictionary dict) - { - dict.PrintDictionary(config, sb, indent); - return; - } - - if (obj is IEnumerable enumerable && obj is not string) - { - enumerable.PrintEnumerable(config, sb, indent); - return; - } - - if (type.IsPrimitive || obj is string || type.GetProperties().Length == 0) - { - sb.Append(obj); - return; - } - - PrintDefaultObject(obj, config, sb, indent); - } - - private static void PrintDefaultObject(object obj, PrintingConfig config, - StringBuilder sb, int indent) - { - var type = obj.GetType(); - - sb.AppendLine(type.Name + ":"); - - foreach (var prop in type.GetProperties()) - { - var name = prop.Name; - var propType = prop.PropertyType; - - if (config.ExcludedProperties.Contains(name) || - config.ExcludedTypes.Contains(propType)) - continue; - - var value = prop.GetValue(obj); - - sb.Append(new string('\t', indent + 1)); - sb.Append(name); - sb.Append(" = "); - - if (config.PropertySerializers.TryGetValue(name, out var propSer)) - { - sb.AppendLine(value != null ? propSer(value) : "null"); - continue; - } - - if (value is string s && - config.StringTrimmingRules.TryGetValue(name, out var max)) - { - sb.AppendLine(s.Length <= max ? s : s[..max]); - continue; - } - - if (value is null) - { - sb.AppendLine("null"); - continue; - } - - value.Print(config, sb, indent + 1); - sb.AppendLine(); - } - } -} diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index bf7335edb..c534ec2bd 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -1,3 +1,5 @@ +using ObjectPrinting.Extensions; + namespace ObjectPrinting; public static class ObjectPrinter diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index d8e84a9e8..041336e19 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; -using System.Text; +using ObjectPrinting.Configs; +using ObjectPrinting.Extensions; namespace ObjectPrinting; @@ -22,7 +23,7 @@ public PrintingConfig Excluding() } public PrintingConfig Excluding(Expression> selector) { - ExcludedProperties.Add(GetPropertyName(selector)); + ExcludedProperties.Add(PrintingConfigExtensions.GetPropertyName(selector)); return this; } @@ -36,23 +37,4 @@ public PropertyPrintingConfig Printing( { return new PropertyPrintingConfig(this, selector); } - - internal static string GetPropertyName(Expression> selector) - { - return selector.Body switch - { - MemberExpression m => m.Member.Name, - UnaryExpression { Operand: MemberExpression m2 } => m2.Member.Name, - _ => throw new ArgumentException("Expression must be a property") - }; - } - - public string PrintToString(TOwner obj) - { - var sb = new StringBuilder(); - Visited.Clear(); - if (obj != null) - obj.Print(this, sb, 0); - return sb.ToString(); - } } \ No newline at end of file diff --git a/ObjectPrinting/Solved/ObjectExtensions.cs b/ObjectPrinting/Solved/ObjectExtensions.cs deleted file mode 100644 index b0c94553c..000000000 --- a/ObjectPrinting/Solved/ObjectExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ObjectPrinting.Solved -{ - public static class ObjectExtensions - { - public static string PrintToString(this T obj) - { - return ObjectPrinter.For().PrintToString(obj); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/ObjectPrinter.cs b/ObjectPrinting/Solved/ObjectPrinter.cs deleted file mode 100644 index 540ee769c..000000000 --- a/ObjectPrinting/Solved/ObjectPrinter.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ObjectPrinting.Solved -{ - public class ObjectPrinter - { - public static PrintingConfig For() - { - return new PrintingConfig(); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/PrintingConfig.cs b/ObjectPrinting/Solved/PrintingConfig.cs deleted file mode 100644 index 0ec5aeb2b..000000000 --- a/ObjectPrinting/Solved/PrintingConfig.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Text; - -namespace ObjectPrinting.Solved -{ - 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(); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/PropertyPrintingConfig.cs b/ObjectPrinting/Solved/PropertyPrintingConfig.cs deleted file mode 100644 index a509697d1..000000000 --- a/ObjectPrinting/Solved/PropertyPrintingConfig.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Globalization; - -namespace ObjectPrinting.Solved -{ - public class PropertyPrintingConfig : IPropertyPrintingConfig - { - 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; - } - - public interface IPropertyPrintingConfig - { - PrintingConfig ParentConfig { get; } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs deleted file mode 100644 index dd3922394..000000000 --- a/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace ObjectPrinting.Solved -{ - 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) - { - return ((IPropertyPrintingConfig)propConfig).ParentConfig; - } - - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs deleted file mode 100644 index ac52d5ee5..000000000 --- a/ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Globalization; -using NUnit.Framework; - -namespace ObjectPrinting.Solved.Tests -{ - [TestFixture] - public class ObjectPrinterAcceptanceTests - { - [Test] - public void Demo() - { - var person = new Person { Name = "Alex", Age = 19 }; - - var printer = ObjectPrinter.For() - //1. Исключить из сериализации свойства определенного типа - .Excluding() - //2. Указать альтернативный способ сериализации для определенного типа - .Printing().Using(i => i.ToString("X")) - //3. Для числовых типов указать культуру - .Printing().Using(CultureInfo.InvariantCulture) - //4. Настроить сериализацию конкретного свойства - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - .Printing(p => p.Name).TrimmedToLength(10) - //6. Исключить из сериализации конкретного свойства - .Excluding(p => p.Age); - - string s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - string s2 = person.PrintToString(); - - //8. ...с конфигурированием - string s3 = person.PrintToString(s => s.Excluding(p => p.Age)); - Console.WriteLine(s1); - Console.WriteLine(s2); - Console.WriteLine(s3); - } - } -} \ 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/Strategies/DefaultObjectStrategy.cs b/ObjectPrinting/Strategies/DefaultObjectStrategy.cs new file mode 100644 index 000000000..f1046c11c --- /dev/null +++ b/ObjectPrinting/Strategies/DefaultObjectStrategy.cs @@ -0,0 +1,55 @@ +using System; +using System.Text; +using ObjectPrinting.Extensions; + +namespace ObjectPrinting.Strategies; + +public class DefaultObjectStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => true; + + public void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent) + { + sb.AppendLine(type.Name + ":"); + + foreach (var prop in type.GetProperties()) + { + var name = prop.Name; + var propType = prop.PropertyType; + + if (config.ExcludedProperties.Contains(name) || + config.ExcludedTypes.Contains(propType)) + continue; + + var value = prop.GetValue(obj); + + sb.Append(new string('\t', indent + 1)); + sb.Append(name); + sb.Append(" = "); + + if (config.PropertySerializers.TryGetValue(name, out var propSer)) + { + sb.AppendLine(value != null ? propSer(value) : "null"); + continue; + } + + if (value is string s && + config.StringTrimmingRules.TryGetValue(name, out var max)) + { + sb.AppendLine(s.Length <= max ? s : s[..max]); + continue; + } + + if (value is null) + { + sb.AppendLine("null"); + continue; + } + + value.Print(config, sb, indent + 1); + sb.AppendLine(); + } + } +} \ No newline at end of file diff --git a/ObjectPrinting/Strategies/DictionaryStrategy.cs b/ObjectPrinting/Strategies/DictionaryStrategy.cs new file mode 100644 index 000000000..5b6b4c239 --- /dev/null +++ b/ObjectPrinting/Strategies/DictionaryStrategy.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections; +using System.Text; +using ObjectPrinting.Extensions; + +namespace ObjectPrinting.Strategies; + +public class DictionaryStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => obj is IDictionary; + + public void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent) + { + ((IDictionary)obj).PrintDictionary(config, sb, indent); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Strategies/EnumerableStrategy.cs b/ObjectPrinting/Strategies/EnumerableStrategy.cs new file mode 100644 index 000000000..2b7e4492b --- /dev/null +++ b/ObjectPrinting/Strategies/EnumerableStrategy.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections; +using System.Text; +using ObjectPrinting.Extensions; + +namespace ObjectPrinting.Strategies; + +public class EnumerableStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => obj is IEnumerable && obj is not string; + + public void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent) + { + ((IEnumerable)obj).PrintEnumerable(config, sb, indent); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Strategies/ExcludedTypeStrategy.cs b/ObjectPrinting/Strategies/ExcludedTypeStrategy.cs new file mode 100644 index 000000000..47784b5c9 --- /dev/null +++ b/ObjectPrinting/Strategies/ExcludedTypeStrategy.cs @@ -0,0 +1,15 @@ +using System; +using System.Text; + +namespace ObjectPrinting.Strategies; + +public class ExcludedTypeStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => config.ExcludedTypes.Contains(type); + + public void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent) + { + } +} \ No newline at end of file diff --git a/ObjectPrinting/Strategies/SimpleTypeStrategy.cs b/ObjectPrinting/Strategies/SimpleTypeStrategy.cs new file mode 100644 index 000000000..8f911ce02 --- /dev/null +++ b/ObjectPrinting/Strategies/SimpleTypeStrategy.cs @@ -0,0 +1,16 @@ +using System; +using System.Text; + +namespace ObjectPrinting.Strategies; + +public class SimpleTypeStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => type.IsPrimitive || obj is string || type.GetProperties().Length == 0; + + public void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent) + { + sb.Append(obj); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Strategies/TypeSerializerStrategy.cs b/ObjectPrinting/Strategies/TypeSerializerStrategy.cs new file mode 100644 index 000000000..7703a82b0 --- /dev/null +++ b/ObjectPrinting/Strategies/TypeSerializerStrategy.cs @@ -0,0 +1,17 @@ +using System; +using System.Text; + +namespace ObjectPrinting.Strategies; + +public class TypeSerializerStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => config.TypeSerializers.ContainsKey(type); + + public void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent) + { + var serializer = config.TypeSerializers[type]; + sb.Append(serializer(obj)); + } +} \ No newline at end of file From 74fc126b587063dd16cf2ca4d83f952eb7772483 Mon Sep 17 00:00:00 2001 From: maximka200 Date: Tue, 25 Nov 2025 16:36:43 +0500 Subject: [PATCH 08/13] =?UTF-8?q?-=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=D0=BB=20DefaultObjectStrategy,=20=D1=81=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D0=B0=D0=BB=20=D0=B1=D0=BE=D0=BB=D0=B5=D0=B5=20?= =?UTF-8?q?=D1=87=D0=B8=D1=82=D0=B0=D0=B5=D0=BC=D1=8B=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/ObjectPrinter.cs | 1 + .../Strategies/DefaultObjectStrategy.cs | 115 +++++++++++++----- 2 files changed, 84 insertions(+), 32 deletions(-) diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index c534ec2bd..240916516 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -9,6 +9,7 @@ public static PrintingConfig For() return new PrintingConfig(); } + /// Синтаксический сахар, вызывает сериализацию обьекта с дефолтным конфигом public static string PrintToString(object obj) { var printer = new PrintingConfig(); diff --git a/ObjectPrinting/Strategies/DefaultObjectStrategy.cs b/ObjectPrinting/Strategies/DefaultObjectStrategy.cs index f1046c11c..3588f6908 100644 --- a/ObjectPrinting/Strategies/DefaultObjectStrategy.cs +++ b/ObjectPrinting/Strategies/DefaultObjectStrategy.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using System.Text; using ObjectPrinting.Extensions; @@ -9,47 +10,97 @@ public class DefaultObjectStrategy : IPrintStrategy public bool CanPrint(object obj, Type type, PrintingConfig config) => true; - public void Print(object obj, Type type, PrintingConfig config, - StringBuilder sb, int indent) + public void Print(object obj, Type type, + PrintingConfig config, StringBuilder sb, int indent) { sb.AppendLine(type.Name + ":"); foreach (var prop in type.GetProperties()) { - var name = prop.Name; - var propType = prop.PropertyType; - - if (config.ExcludedProperties.Contains(name) || - config.ExcludedTypes.Contains(propType)) + if (ShouldSkipProperty(prop, config)) continue; - var value = prop.GetValue(obj); + PrintProperty(obj, prop, config, sb, indent); + } + } + + #region Helpers + + private static bool ShouldSkipProperty( + PropertyInfo prop, + PrintingConfig config) + { + var name = prop.Name; + var type = prop.PropertyType; - sb.Append(new string('\t', indent + 1)); - sb.Append(name); - sb.Append(" = "); + return config.ExcludedProperties.Contains(name) + || config.ExcludedTypes.Contains(type); + } - if (config.PropertySerializers.TryGetValue(name, out var propSer)) - { - sb.AppendLine(value != null ? propSer(value) : "null"); - continue; - } + private static void PrintProperty(object obj, PropertyInfo prop, + PrintingConfig config, StringBuilder sb, int indent) + { + var name = prop.Name; + var value = prop.GetValue(obj); - if (value is string s && - config.StringTrimmingRules.TryGetValue(name, out var max)) - { - sb.AppendLine(s.Length <= max ? s : s[..max]); - continue; - } + PrintPropertyHeader(sb, indent, name); - if (value is null) - { - sb.AppendLine("null"); - continue; - } - - value.Print(config, sb, indent + 1); - sb.AppendLine(); - } + if (TryPrintWithCustomSerializer(name, value, config, sb)) return; + if (TryPrintStringTrimmed(name, value, config, sb)) return; + if (TryPrintNull(value, sb)) return; + + PrintNestedObject(value, config, sb, indent + 1); + } + + private static void PrintPropertyHeader(StringBuilder sb, int indent, string name) + { + sb.Append(new string('\t', indent + 1)); + sb.Append(name); + sb.Append(" = "); + } + + private static bool TryPrintWithCustomSerializer( + string name, + object? value, + PrintingConfig config, + StringBuilder sb) + { + if (!config.PropertySerializers.TryGetValue(name, out var serializer)) + return false; + + sb.AppendLine(value != null ? serializer(value) : "null"); + return true; + } + + private static bool TryPrintStringTrimmed(string name, object? value, + PrintingConfig config, StringBuilder sb) + { + if (value is not string s || + !config.StringTrimmingRules.TryGetValue(name, out var max)) + return false; + + sb.AppendLine(s.Length <= max ? s : s[..max]); + return true; + } + + private static bool TryPrintNull(object? value, StringBuilder sb) + { + if (value is not null) + return false; + + sb.AppendLine("null"); + return true; + } + + private static void PrintNestedObject( + object? value, + PrintingConfig config, + StringBuilder sb, + int indent) + { + value.Print(config, sb, indent); + sb.AppendLine(); } -} \ No newline at end of file + + #endregion +} From 4df660bed2b09ddfe2f4b27f9b8b33d213808e31 Mon Sep 17 00:00:00 2001 From: maximka200 Date: Tue, 25 Nov 2025 16:58:17 +0500 Subject: [PATCH 09/13] =?UTF-8?q?-=20=D0=BF=D0=BE=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D1=82=D0=B5=D1=81=D1=82=20=D0=BA=D0=B5=D0=B9?= =?UTF-8?q?=D1=81=D1=8B=20-=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=B0=D0=BB=20=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8E=20=D0=BF=D0=B0=D1=80=D1=81=D0=B8=D0=BD=D0=B3=D0=B0?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BB=D0=BB=D0=B5=D0=BA=D1=86=D0=B8=D0=B9(?= =?UTF-8?q?=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=B2=D1=8B=D0=B2=D0=BE?= =?UTF-8?q?=D0=B4=D0=B8=D1=82=D1=81=D1=8F=20=D0=B3=D0=B5=D0=BD=D0=B5=D1=80?= =?UTF-8?q?=D0=B8=D0=BA-=D1=82=D0=B8=D0=BF=20=D0=B8=20=D0=BD=D0=B5=D1=82?= =?UTF-8?q?=20=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D1=85=20=D1=81=D0=B8=D0=BC?= =?UTF-8?q?=D0=B2=D0=BE=D0=BB=D0=BE=D0=B2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExpectedResults/Dictionary_Primitive.txt | 2 +- .../Dictionary_With_ListValues.txt | 6 +-- .../ExpectedResults/List_Of_Lists.txt | 6 +-- .../Person_With_Collections.txt | 4 +- .../ExpectedResults/StringList.txt | 2 +- .../ExpectedResults/Team_With_Members.txt | 10 ++--- .../ObjectPrinterAcceptanceTests.cs | 37 ------------------- .../CollectionPrintingExtensions.cs | 22 ++++++++++- 8 files changed, 35 insertions(+), 54 deletions(-) diff --git a/ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt b/ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt index 078ead0dc..99f62f339 100644 --- a/ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt +++ b/ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt @@ -1,4 +1,4 @@ -Dictionary`2 { +Dictionary { [a] = 1 [b] = 2 } \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/Dictionary_With_ListValues.txt b/ObjectPrinterTests/ExpectedResults/Dictionary_With_ListValues.txt index 3be9434a9..73943dde0 100644 --- a/ObjectPrinterTests/ExpectedResults/Dictionary_With_ListValues.txt +++ b/ObjectPrinterTests/ExpectedResults/Dictionary_With_ListValues.txt @@ -1,9 +1,9 @@ -Dictionary`2 { - [first] = List`1 [ +Dictionary> { + [first] = List [ 1 2 ] - [second] = List`1 [ + [second] = List [ 3 ] } \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/List_Of_Lists.txt b/ObjectPrinterTests/ExpectedResults/List_Of_Lists.txt index 402f29ee8..05a4ae7d0 100644 --- a/ObjectPrinterTests/ExpectedResults/List_Of_Lists.txt +++ b/ObjectPrinterTests/ExpectedResults/List_Of_Lists.txt @@ -1,9 +1,9 @@ -List`1 [ - List`1 [ +List> [ + List [ 1 2 ] - List`1 [ + List [ 3 ] ] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/Person_With_Collections.txt b/ObjectPrinterTests/ExpectedResults/Person_With_Collections.txt index e3a30a028..e989f3e2c 100644 --- a/ObjectPrinterTests/ExpectedResults/Person_With_Collections.txt +++ b/ObjectPrinterTests/ExpectedResults/Person_With_Collections.txt @@ -4,11 +4,11 @@ Person: 10 20 ] - Tags = List`1 [ + Tags = List [ dev qa ] - Addresses = Dictionary`2 { + Addresses = Dictionary { [home] = Address: City = Chelyabinsk diff --git a/ObjectPrinterTests/ExpectedResults/StringList.txt b/ObjectPrinterTests/ExpectedResults/StringList.txt index d1e44cd06..f9df8ff41 100644 --- a/ObjectPrinterTests/ExpectedResults/StringList.txt +++ b/ObjectPrinterTests/ExpectedResults/StringList.txt @@ -1,4 +1,4 @@ -List`1 [ +List [ one two ] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/Team_With_Members.txt b/ObjectPrinterTests/ExpectedResults/Team_With_Members.txt index 8756429fb..bee4e0100 100644 --- a/ObjectPrinterTests/ExpectedResults/Team_With_Members.txt +++ b/ObjectPrinterTests/ExpectedResults/Team_With_Members.txt @@ -1,15 +1,15 @@ Team: Title = My_Command - Members = List`1 [ + Members = List [ Person: Name = Max Scores = Int32[] [ 1 ] - Tags = List`1 [ + Tags = List [ dev ] - Addresses = Dictionary`2 { + Addresses = Dictionary { } Person: @@ -18,10 +18,10 @@ Team: 2 3 ] - Tags = List`1 [ + Tags = List [ qa ] - Addresses = Dictionary`2 { + Addresses = Dictionary { } ] diff --git a/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs index 86ebc2b0d..1fbb37b76 100644 --- a/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs @@ -5,43 +5,6 @@ namespace ObjectPrinterTests { - [TestFixture] - public class ObjectPrinterAcceptanceTests - { - private class Person - { - public Guid Id { get; set; } - public string Name { get; set; } - public double Height { get; set; } - public int Age { get; set; } - - public Person Child { get; set; } - } - - [Test] - public void Demo() - { - var person = new Person {Id = new Guid(), Name = "Alex", Age = 112, Height = 1.89, - Child = new Person {Id = new Guid(), Name = "Roman", Age = 5, Height = 1.0} - }; - - var printer = ObjectPrinter.For() - .Printing().Using(x => (x - 1).ToString()) - .Printing().Using(CultureInfo.InvariantCulture) - .Printing(p => p.Name).TrimmedToLength(3) - .Excluding(p => p.Id) - .Excluding(); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - var s2 = ObjectPrinter.PrintToString(person); - //8. ...с конфигурированием - var s1 = printer.PrintToString(person); - - Console.WriteLine(s1); - Console.WriteLine("-----"); - Console.WriteLine(s2); - } - } [TestFixture] public class ObjectPrinterTests { diff --git a/ObjectPrinting/Extensions/CollectionPrintingExtensions.cs b/ObjectPrinting/Extensions/CollectionPrintingExtensions.cs index edc550279..9e49e03f8 100644 --- a/ObjectPrinting/Extensions/CollectionPrintingExtensions.cs +++ b/ObjectPrinting/Extensions/CollectionPrintingExtensions.cs @@ -1,4 +1,6 @@ +using System; using System.Collections; +using System.Linq; using System.Text; namespace ObjectPrinting.Extensions; @@ -8,7 +10,7 @@ public static class CollectionPrintingExtensions public static void PrintDictionary(this IDictionary dict, PrintingConfig config, StringBuilder sb, int indent) { - sb.AppendLine(dict.GetType().Name + " {"); + sb.AppendLine(GetTypeName(dict.GetType()) + " {"); foreach (DictionaryEntry entry in dict) { sb.Append(new string('\t', indent + 1)); @@ -34,7 +36,7 @@ public static void PrintDictionary(this IDictionary dict, PrintingConfig public static void PrintEnumerable(this IEnumerable enumerable, PrintingConfig config, StringBuilder sb, int indent) { - sb.AppendLine(enumerable.GetType().Name + " ["); + sb.AppendLine(GetTypeName(enumerable.GetType()) + " ["); foreach (var item in enumerable) { sb.Append(new string('\t', indent + 1)); @@ -52,4 +54,20 @@ public static void PrintEnumerable(this IEnumerable enumerable, Printing sb.Append(new string('\t', indent)); sb.Append(']'); } + + private static string GetTypeName(Type type) + { + if (!type.IsGenericType) + return type.Name; + + var name = type.Name; + var backtickIndex = name.IndexOf('`'); + if (backtickIndex > 0) + name = name[..backtickIndex]; + + var genericArgs = type.GetGenericArguments() + .Select(GetTypeName); + + return $"{name}<{string.Join(", ", genericArgs)}>"; + } } \ No newline at end of file From 010eed5fa15c45f0fc726412f8d3d28ed9ab3c0c Mon Sep 17 00:00:00 2001 From: maximka200 Date: Tue, 25 Nov 2025 23:19:47 +0500 Subject: [PATCH 10/13] =?UTF-8?q?-=20=D0=B2=D0=B5=D1=80=D0=BD=D1=83=D0=BB?= =?UTF-8?q?=20fluent-api.sln?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fluent-api.sln | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 fluent-api.sln diff --git a/fluent-api.sln b/fluent-api.sln new file mode 100644 index 000000000..9ce96dc6a --- /dev/null +++ b/fluent-api.sln @@ -0,0 +1,56 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrinting", "ObjectPrinting\ObjectPrinting.csproj", "{07B8C9B7-8289-46CB-9875-048A57758EEE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{6D308E4A-CEC7-4536-9B87-81CD337A87AD}" + ProjectSection(SolutionItems) = preProject + Samples\.gitignore = Samples\.gitignore + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping", "Samples\FluentMapper\FluentMapping.csproj", "{FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping.Tests", "Samples\FluentMapper.Tests\FluentMapping.Tests.csproj", "{8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectacle", "Samples\Spectacle\Spectacle.csproj", "{EFA9335C-411B-4597-B0B6-5438D1AE04C3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrinterTests", "ObjectPrinterTests\ObjectPrinterTests.csproj", "{4C8905B7-BFC8-4E4A-9DA1-B24435405856}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {07B8C9B7-8289-46CB-9875-048A57758EEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07B8C9B7-8289-46CB-9875-048A57758EEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07B8C9B7-8289-46CB-9875-048A57758EEE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07B8C9B7-8289-46CB-9875-048A57758EEE}.Release|Any CPU.Build.0 = Release|Any CPU + {FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}.Release|Any CPU.Build.0 = Release|Any CPU + {8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D}.Release|Any CPU.Build.0 = Release|Any CPU + {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {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 + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E} = {6D308E4A-CEC7-4536-9B87-81CD337A87AD} + {8A7BB3EA-3E6A-4D04-A801-D5CD1620DA0D} = {6D308E4A-CEC7-4536-9B87-81CD337A87AD} + {EFA9335C-411B-4597-B0B6-5438D1AE04C3} = {6D308E4A-CEC7-4536-9B87-81CD337A87AD} + EndGlobalSection +EndGlobal From 0c335d3b0db3de8c041818e893764d02eb46c5e8 Mon Sep 17 00:00:00 2001 From: maximka200 Date: Tue, 25 Nov 2025 23:27:27 +0500 Subject: [PATCH 11/13] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20fluent-api.sln=20=D0=B2=20.gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a1f466689..def330ce9 100644 --- a/.gitignore +++ b/.gitignore @@ -236,3 +236,5 @@ _Pvt_Extensions .fake/ .idea/ + +fluent-api.sln From 71f8cca2ed779a8beb3a5acf041c9123db2ebb74 Mon Sep 17 00:00:00 2001 From: maximka200 Date: Tue, 25 Nov 2025 23:38:53 +0500 Subject: [PATCH 12/13] =?UTF-8?q?-=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=D0=BB=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D1=81=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=D0=BC=20Verify?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinterTests/CollectionPrintingTests.cs | 116 +++++----- ObjectPrinterTests/Entities/A.cs | 9 + ObjectPrinterTests/Entities/Address.cs | 7 + ObjectPrinterTests/Entities/B.cs | 7 + ObjectPrinterTests/Entities/Person.cs | 9 + ...apshotTests.IntArray_Snapshot.verified.txt | 5 + ...ictionary_ShouldHandleCorrect.verified.txt | 4 + ...assWithCollections_Correctly.verified.txt} | 4 +- ...naryWithListValues_Correctly.verified.txt} | 2 +- ...eDictionaryWithNull_Correctly.verified.txt | 4 + ...dHandleListOfLists_Correctly.verified.txt} | 2 +- ...dHandleListWithNull_Correctly.verified.txt | 5 + ...tringList_ShouldHandleCorrect.verified.txt | 4 + .../ExpectedResults/Dictionary_Primitive.txt | 4 - .../ExpectedResults/IntArray.txt | 5 - ...uldApplyCultureInfo_Correctly.verified.txt | 5 + ...lizationForProperty_Correctly.verified.txt | 5 + ...udeSpecificProperty_Correctly.verified.txt | 4 + ...g_ShouldExcludeType_Correctly.verified.txt | 4 + ...dleCyclicReferences_Correctly.verified.txt | 11 + ...uldHandleNullObject_Correctly.verified.txt | 1 + ...ng_ShouldTrimString_Correctly.verified.txt | 5 + ...CustomSerialization_Correctly.verified.txt | 5 + .../ExpectedResults/StringList.txt | 4 - .../ExpectedResults/Team_With_Members.txt | 27 --- ObjectPrinterTests/Helper.cs | 17 -- .../ObjectPrinterAcceptanceTests.cs | 207 +++++++++--------- ObjectPrinterTests/ObjectPrinterTests.csproj | 3 +- ObjectPrinting/Extensions/ObjectExtensions.cs | 2 +- .../Extensions/PrintingConfigExtensions.cs | 4 +- ObjectPrinting/ObjectPrinter.cs | 2 +- ObjectPrinting/PrintingConfig.cs | 2 +- fluent-api.sln.DotSettings | 86 ++++++++ 33 files changed, 357 insertions(+), 224 deletions(-) create mode 100644 ObjectPrinterTests/Entities/A.cs create mode 100644 ObjectPrinterTests/Entities/Address.cs create mode 100644 ObjectPrinterTests/Entities/B.cs create mode 100644 ObjectPrinterTests/Entities/Person.cs create mode 100644 ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.IntArray_Snapshot.verified.txt create mode 100644 ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_Dictionary_ShouldHandleCorrect.verified.txt rename ObjectPrinterTests/ExpectedResults/{Person_With_Collections.txt => CollectionPrintingSnapshotTests.PrintToString_ShouldHandleClassWithCollections_Correctly.verified.txt} (79%) rename ObjectPrinterTests/ExpectedResults/{Dictionary_With_ListValues.txt => CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithListValues_Correctly.verified.txt} (65%) create mode 100644 ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithNull_Correctly.verified.txt rename ObjectPrinterTests/ExpectedResults/{List_Of_Lists.txt => CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListOfLists_Correctly.verified.txt} (68%) create mode 100644 ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListWithNull_Correctly.verified.txt create mode 100644 ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_StringList_ShouldHandleCorrect.verified.txt delete mode 100644 ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt delete mode 100644 ObjectPrinterTests/ExpectedResults/IntArray.txt create mode 100644 ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCultureInfo_Correctly.verified.txt create mode 100644 ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCustomSerializationForProperty_Correctly.verified.txt create mode 100644 ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeSpecificProperty_Correctly.verified.txt create mode 100644 ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeType_Correctly.verified.txt create mode 100644 ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleCyclicReferences_Correctly.verified.txt create mode 100644 ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleNullObject_Correctly.verified.txt create mode 100644 ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldTrimString_Correctly.verified.txt create mode 100644 ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldUseCustomSerialization_Correctly.verified.txt delete mode 100644 ObjectPrinterTests/ExpectedResults/StringList.txt delete mode 100644 ObjectPrinterTests/ExpectedResults/Team_With_Members.txt delete mode 100644 ObjectPrinterTests/Helper.cs create mode 100644 fluent-api.sln.DotSettings diff --git a/ObjectPrinterTests/CollectionPrintingTests.cs b/ObjectPrinterTests/CollectionPrintingTests.cs index 292283ec0..0582bfe29 100644 --- a/ObjectPrinterTests/CollectionPrintingTests.cs +++ b/ObjectPrinterTests/CollectionPrintingTests.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using ObjectPrinterTests.Entities; using ObjectPrinting; using ObjectPrinting.Extensions; @@ -7,51 +7,38 @@ namespace ObjectPrinterTests; [TestFixture] public class CollectionPrintingSnapshotTests { - private class Address + private static readonly VerifySettings SnapshotSettings; + + static CollectionPrintingSnapshotTests() { - public string City { get; set; } = ""; + SnapshotSettings = new VerifySettings(); + SnapshotSettings.UseDirectory("ExpectedResults"); } - - private class Person - { - public string Name { get; set; } = ""; - public int[] Scores { get; set; } = []; - public List Tags { get; set; } = []; - public Dictionary Addresses { get; set; } = new(); - } - - private class Team - { - public string Title { get; set; } = ""; - public List Members { get; set; } = new(); - } - + [Test] - public void IntArray_Snapshot() + public Task IntArray_Snapshot() { var config = new PrintingConfig(); var value = new[] { 1, 2, 3 }; - + var actual = config.PrintToString(value); - var expected = Helper.Read("IntArray.txt"); - - actual.Should().Be(expected); + + return Verify(actual, SnapshotSettings); } [Test] - public void StringList_Snapshot() + public Task PrintToString_StringList_ShouldHandleCorrect() { var config = new PrintingConfig>(); var value = new List { "one", "two" }; var actual = config.PrintToString(value); - var expected = Helper.Read("StringList.txt"); - actual.Should().Be(expected); + return Verify(actual, SnapshotSettings); } [Test] - public void Dictionary_Primitive_Snapshot() + public Task PrintToString_Dictionary_ShouldHandleCorrect() { var config = new PrintingConfig>(); var value = new Dictionary @@ -61,13 +48,12 @@ public void Dictionary_Primitive_Snapshot() }; var actual = config.PrintToString(value); - var expected = Helper.Read("Dictionary_Primitive.txt"); - actual.Should().Be(expected); + return Verify(actual, SnapshotSettings); } [Test] - public void PersonWithCollections_Snapshot() + public Task PrintToString_ShouldHandleClassWithCollections_Correctly() { var config = new PrintingConfig(); var person = new Person @@ -83,13 +69,12 @@ public void PersonWithCollections_Snapshot() }; var actual = config.PrintToString(person); - var expected = Helper.Read("Person_With_Collections.txt"); - actual.Should().Be(expected); + return Verify(actual, SnapshotSettings); } [Test] - public void List_Of_Lists_Snapshot() + public Task PrintToString_ShouldHandleListOfLists_Correctly() { var config = new PrintingConfig>>(); var value = new List> @@ -99,13 +84,12 @@ public void List_Of_Lists_Snapshot() }; var actual = config.PrintToString(value); - var expected = Helper.Read("List_Of_Lists.txt"); - actual.Should().Be(expected); + return Verify(actual, SnapshotSettings); } [Test] - public void DictionaryWithListValues_Snapshot() + public Task PrintToString_ShouldHandleDictionaryWithListValues_Correctly() { var config = new PrintingConfig>>(); var value = new Dictionary> @@ -115,38 +99,50 @@ public void DictionaryWithListValues_Snapshot() }; var actual = config.PrintToString(value); - var expected = Helper.Read("Dictionary_With_ListValues.txt"); - actual.Should().Be(expected); + return Verify(actual, SnapshotSettings); } + + [Test] + public Task PrintToString_ShouldHandleDictionaryWithNull_Correctly() + { + var config = new PrintingConfig>>(); + var value = new Dictionary?> + { + ["first"] = null, + ["second"] = null + }; + var actual = config.PrintToString(value); + + return Verify(actual, SnapshotSettings); + } + [Test] - public void TeamWithMembers_Snapshot() + public Task PrintToString_ShouldHandleListWithNull_Correctly() { - var config = new PrintingConfig(); - var team = new Team + var config = new PrintingConfig>>(); + var value = new List() { - Title = "My_Command", - Members = - { - new Person - { - Name = "Max", - Scores = [1], - Tags = ["dev"] - }, - new Person - { - Name = "Kate", - Scores = [2, 3], - Tags = ["qa"] - } - } + null, + null, + null }; - var actual = config.PrintToString(team); - var expected = Helper.Read("Team_With_Members.txt"); + var actual = config.PrintToString(value); + + return Verify(actual, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldHandleListCycle_Correctly() + { + var config = new PrintingConfig>(); + var value = new List(); + value.Add(value); + + var actual = config.PrintToString(value); - actual.Should().Be(expected); + return Verify(actual, SnapshotSettings); } } diff --git a/ObjectPrinterTests/Entities/A.cs b/ObjectPrinterTests/Entities/A.cs new file mode 100644 index 000000000..d771f6be6 --- /dev/null +++ b/ObjectPrinterTests/Entities/A.cs @@ -0,0 +1,9 @@ +namespace ObjectPrinterTests.Entities; + +public class A +{ + public string Name { get; set; } + public int Number { get; set; } + public double Price { get; set; } + public Guid Id { get; set; } +} \ No newline at end of file diff --git a/ObjectPrinterTests/Entities/Address.cs b/ObjectPrinterTests/Entities/Address.cs new file mode 100644 index 000000000..1b3942ce2 --- /dev/null +++ b/ObjectPrinterTests/Entities/Address.cs @@ -0,0 +1,7 @@ +namespace ObjectPrinterTests.Entities; + +public class Address +{ + public string City { get; set; } = ""; + public string Street { get; set; } = ""; +} \ No newline at end of file diff --git a/ObjectPrinterTests/Entities/B.cs b/ObjectPrinterTests/Entities/B.cs new file mode 100644 index 000000000..c4395047d --- /dev/null +++ b/ObjectPrinterTests/Entities/B.cs @@ -0,0 +1,7 @@ +namespace ObjectPrinterTests.Entities; + +public class B +{ + public A Data { get; set; } + public B Parent { get; set; } +} \ No newline at end of file diff --git a/ObjectPrinterTests/Entities/Person.cs b/ObjectPrinterTests/Entities/Person.cs new file mode 100644 index 000000000..88d12122c --- /dev/null +++ b/ObjectPrinterTests/Entities/Person.cs @@ -0,0 +1,9 @@ +namespace ObjectPrinterTests.Entities; + +public class Person +{ + public string Name { get; set; } = ""; + public int[] Scores { get; set; } = []; + public List Tags { get; set; } = []; + public Dictionary Addresses { get; set; } = new(); +} \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.IntArray_Snapshot.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.IntArray_Snapshot.verified.txt new file mode 100644 index 000000000..7a2e38be0 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.IntArray_Snapshot.verified.txt @@ -0,0 +1,5 @@ +Int32[] [ + 1 + 2 + 3 +] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_Dictionary_ShouldHandleCorrect.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_Dictionary_ShouldHandleCorrect.verified.txt new file mode 100644 index 000000000..74d1a37f3 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_Dictionary_ShouldHandleCorrect.verified.txt @@ -0,0 +1,4 @@ +Dictionary { + [a] = 1 + [b] = 2 +} diff --git a/ObjectPrinterTests/ExpectedResults/Person_With_Collections.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleClassWithCollections_Correctly.verified.txt similarity index 79% rename from ObjectPrinterTests/ExpectedResults/Person_With_Collections.txt rename to ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleClassWithCollections_Correctly.verified.txt index e989f3e2c..45bb773da 100644 --- a/ObjectPrinterTests/ExpectedResults/Person_With_Collections.txt +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleClassWithCollections_Correctly.verified.txt @@ -1,4 +1,4 @@ -Person: +Person: Name = John Scores = Int32[] [ 10 @@ -11,8 +11,10 @@ Person: Addresses = Dictionary { [home] = Address: City = Chelyabinsk + Street = [work] = Address: City = Sverdlovsk + Street = } diff --git a/ObjectPrinterTests/ExpectedResults/Dictionary_With_ListValues.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithListValues_Correctly.verified.txt similarity index 65% rename from ObjectPrinterTests/ExpectedResults/Dictionary_With_ListValues.txt rename to ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithListValues_Correctly.verified.txt index 73943dde0..010004b06 100644 --- a/ObjectPrinterTests/ExpectedResults/Dictionary_With_ListValues.txt +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithListValues_Correctly.verified.txt @@ -1,4 +1,4 @@ -Dictionary> { +Dictionary> { [first] = List [ 1 2 diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithNull_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithNull_Correctly.verified.txt new file mode 100644 index 000000000..2679fce74 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithNull_Correctly.verified.txt @@ -0,0 +1,4 @@ +Dictionary> { + [first] = null + [second] = null +} \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/List_Of_Lists.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListOfLists_Correctly.verified.txt similarity index 68% rename from ObjectPrinterTests/ExpectedResults/List_Of_Lists.txt rename to ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListOfLists_Correctly.verified.txt index 05a4ae7d0..04c060714 100644 --- a/ObjectPrinterTests/ExpectedResults/List_Of_Lists.txt +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListOfLists_Correctly.verified.txt @@ -1,4 +1,4 @@ -List> [ +List> [ List [ 1 2 diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListWithNull_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListWithNull_Correctly.verified.txt new file mode 100644 index 000000000..2ab2c6367 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListWithNull_Correctly.verified.txt @@ -0,0 +1,5 @@ +List [ + null + null + null +] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_StringList_ShouldHandleCorrect.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_StringList_ShouldHandleCorrect.verified.txt new file mode 100644 index 000000000..71c9e25b5 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_StringList_ShouldHandleCorrect.verified.txt @@ -0,0 +1,4 @@ +List [ + one + two +] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt b/ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt deleted file mode 100644 index 99f62f339..000000000 --- a/ObjectPrinterTests/ExpectedResults/Dictionary_Primitive.txt +++ /dev/null @@ -1,4 +0,0 @@ -Dictionary { - [a] = 1 - [b] = 2 -} \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/IntArray.txt b/ObjectPrinterTests/ExpectedResults/IntArray.txt deleted file mode 100644 index 087272e3e..000000000 --- a/ObjectPrinterTests/ExpectedResults/IntArray.txt +++ /dev/null @@ -1,5 +0,0 @@ -Int32[] [ - 1 - 2 - 3 -] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCultureInfo_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCultureInfo_Correctly.verified.txt new file mode 100644 index 000000000..df311f04c --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCultureInfo_Correctly.verified.txt @@ -0,0 +1,5 @@ +A: + Name = null + Number = 0 + Price = 1234,56 + Id = 00000000-0000-0000-0000-000000000000 diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCustomSerializationForProperty_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCustomSerializationForProperty_Correctly.verified.txt new file mode 100644 index 000000000..0112de9df --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCustomSerializationForProperty_Correctly.verified.txt @@ -0,0 +1,5 @@ +A: + Name = NAME=Alex + Number = 0 + Price = 0 + Id = 00000000-0000-0000-0000-000000000000 diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeSpecificProperty_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeSpecificProperty_Correctly.verified.txt new file mode 100644 index 000000000..2a69eef1d --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeSpecificProperty_Correctly.verified.txt @@ -0,0 +1,4 @@ +A: + Name = Alex + Price = 0 + Id = 00000000-0000-0000-0000-000000000000 diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeType_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeType_Correctly.verified.txt new file mode 100644 index 000000000..2c706848f --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeType_Correctly.verified.txt @@ -0,0 +1,4 @@ +A: + Name = Alex + Number = 0 + Price = 0 diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleCyclicReferences_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleCyclicReferences_Correctly.verified.txt new file mode 100644 index 000000000..f29a70f80 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleCyclicReferences_Correctly.verified.txt @@ -0,0 +1,11 @@ +B: + Data = A: + Name = Child + Number = 0 + Price = 0 + Id = 00000000-0000-0000-0000-000000000000 + + Parent = B: + Data = null + Parent = + diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleNullObject_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleNullObject_Correctly.verified.txt new file mode 100644 index 000000000..c296c2eef --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleNullObject_Correctly.verified.txt @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldTrimString_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldTrimString_Correctly.verified.txt new file mode 100644 index 000000000..e9434b485 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldTrimString_Correctly.verified.txt @@ -0,0 +1,5 @@ +A: + Name = Alex + Number = 0 + Price = 0 + Id = 00000000-0000-0000-0000-000000000000 diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldUseCustomSerialization_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldUseCustomSerialization_Correctly.verified.txt new file mode 100644 index 000000000..cddf924bc --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldUseCustomSerialization_Correctly.verified.txt @@ -0,0 +1,5 @@ +A: + Name = null + Number = INT(42) + Price = 0 + Id = 00000000-0000-0000-0000-000000000000 diff --git a/ObjectPrinterTests/ExpectedResults/StringList.txt b/ObjectPrinterTests/ExpectedResults/StringList.txt deleted file mode 100644 index f9df8ff41..000000000 --- a/ObjectPrinterTests/ExpectedResults/StringList.txt +++ /dev/null @@ -1,4 +0,0 @@ -List [ - one - two -] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/Team_With_Members.txt b/ObjectPrinterTests/ExpectedResults/Team_With_Members.txt deleted file mode 100644 index bee4e0100..000000000 --- a/ObjectPrinterTests/ExpectedResults/Team_With_Members.txt +++ /dev/null @@ -1,27 +0,0 @@ -Team: - Title = My_Command - Members = List [ - Person: - Name = Max - Scores = Int32[] [ - 1 - ] - Tags = List [ - dev - ] - Addresses = Dictionary { - } - - Person: - Name = Kate - Scores = Int32[] [ - 2 - 3 - ] - Tags = List [ - qa - ] - Addresses = Dictionary { - } - - ] diff --git a/ObjectPrinterTests/Helper.cs b/ObjectPrinterTests/Helper.cs deleted file mode 100644 index 2175daf72..000000000 --- a/ObjectPrinterTests/Helper.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace ObjectPrinterTests; - -public static class Helper -{ - public static string Read(string fileName) - { - var testDir = TestContext.CurrentContext.TestDirectory; - - var dir = Path.Combine(testDir, "ExpectedResults"); - - var path = Path.Combine(dir, fileName); - if (!File.Exists(path)) - Assert.Fail($"File not found: {path}"); - - return File.ReadAllText(path); - } -} \ No newline at end of file diff --git a/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs index 1fbb37b76..93acbd7d8 100644 --- a/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs @@ -1,118 +1,129 @@ using System.Globalization; -using FluentAssertions; +using ObjectPrinterTests.Entities; using ObjectPrinting; using ObjectPrinting.Extensions; -namespace ObjectPrinterTests +namespace ObjectPrinterTests; + +[TestFixture] +public class ObjectPrinterTests { - [TestFixture] - public class ObjectPrinterTests + private static readonly VerifySettings SnapshotSettings; + + static ObjectPrinterTests() { - private class A - { - public string Name { get; set; } - public int Number { get; set; } - public double Price { get; set; } - public Guid Id { get; set; } - } + SnapshotSettings = new VerifySettings(); + SnapshotSettings.UseDirectory("ExpectedResults"); + } + + [Test] + public Task PrintToString_ShouldHandleNullObject_Correctly() + { + A? obj = null; - private class B - { - public A Data { get; set; } - public B Parent { get; set; } - } - - [Test] - public void ShouldExcludeType_FromSerialization() + var result = ObjectPrinter.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldExcludeType_Correctly() + { + var obj = new A { - var obj = new A { Id = Guid.NewGuid(), Name = "Alex" }; - var printer = ObjectPrinter.For() - .Excluding(); + Id = Guid.Parse("11111111-1111-1111-1111-111111111111"), + Name = "Alex" + }; - var result = printer.PrintToString(obj); + var printer = ObjectPrinter.For() + .Excluding(); - result.Should().NotContain(nameof(A.Id)); - } - - [Test] - public void ShouldUseCustomSerialization_ForType() - { - var obj = new A { Number = 42 }; - var printer = ObjectPrinter.For() - .Printing() - .Using(x => $"INT({x})"); + var result = printer.PrintToString(obj); - var result = printer.PrintToString(obj); + return Verify(result, SnapshotSettings); + } - result.Should().Contain("INT(42)"); - } - - [Test] - public void ShouldApplyCultureInfo_ToType() - { - var obj = new A { Price = 1234.56 }; - var printer = ObjectPrinter.For() - .Printing() - .Using(CultureInfo.GetCultureInfo("fr-FR")); + [Test] + public Task PrintToString_ShouldUseCustomSerialization_Correctly() + { + var obj = new A { Number = 42 }; - var result = printer.PrintToString(obj); + var printer = ObjectPrinter.For() + .Printing() + .Using(x => $"INT({x})"); - result.Should().Contain("1234,56"); - } - - [Test] - public void ShouldApplyСustomSerialization_ToProperty() - { - var obj = new A { Name = "Alex" }; - var printer = ObjectPrinter.For() - .Printing(a => a.Name) - .Using(n => $"NAME={n}"); + var result = printer.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } - var result = printer.PrintToString(obj); + [Test] + public Task PrintToString_ShouldApplyCultureInfo_Correctly() + { + var obj = new A { Price = 1234.56 }; - result.Should().Contain("NAME=Alex"); - } - - [Test] - public void ShouldTrimString_Property() - { - var obj = new A { Name = "Alexander" }; - var printer = ObjectPrinter.For() - .Printing(a => a.Name) - .TrimmedToLength(4); + var printer = ObjectPrinter.For() + .Printing() + .Using(CultureInfo.GetCultureInfo("fr-FR")); - var result = printer.PrintToString(obj); + var result = printer.PrintToString(obj); - result.Should().Contain("Alex"); - result.Should().NotContain("Alexander"); - } - - [Test] - public void ShouldExcludeSpecific_Property() - { - var obj = new A { Name = "Alex", Number = 10 }; - var printer = ObjectPrinter.For() - .Excluding(a => a.Number); - - var result = printer.PrintToString(obj); - - result.Should().NotContain(nameof(A.Number)); - } + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldApplyCustomSerializationForProperty_Correctly() + { + var obj = new A { Name = "Alex" }; + + var printer = ObjectPrinter.For() + .Printing(a => a.Name) + .Using(n => $"NAME={n}"); + + var result = printer.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldTrimString_Correctly() + { + var obj = new A { Name = "Alexander" }; + + var printer = ObjectPrinter.For() + .Printing(a => a.Name) + .TrimmedToLength(4); + + var result = printer.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldExcludeSpecificProperty_Correctly() + { + var obj = new A { Name = "Alex", Number = 10 }; + + var printer = ObjectPrinter.For() + .Excluding(a => a.Number); + + var result = printer.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldHandleCyclicReferences_Correctly() + { + var parent = new B(); + var child = new B { Parent = parent }; + parent.Data = new A { Name = "Child" }; + parent.Parent = child; + + var printer = ObjectPrinter.For(); + + var result = printer.PrintToString(parent); - [Test] - public void ShouldHandleCyclicReferences_InProperty() - { - var parent = new B(); - var child = new B { Parent = parent }; - parent.Data = new A { Name = "Child" }; - parent.Parent = child; - var printer = ObjectPrinter.For(); - - var act = () => printer.PrintToString(parent); - var result = printer.PrintToString(parent); - - act.Should().NotThrow(); - result.Should().Contain(""); - } + return Verify(result, SnapshotSettings); } -} \ No newline at end of file +} diff --git a/ObjectPrinterTests/ObjectPrinterTests.csproj b/ObjectPrinterTests/ObjectPrinterTests.csproj index dc435c70c..13b2812be 100644 --- a/ObjectPrinterTests/ObjectPrinterTests.csproj +++ b/ObjectPrinterTests/ObjectPrinterTests.csproj @@ -11,9 +11,10 @@ - + + diff --git a/ObjectPrinting/Extensions/ObjectExtensions.cs b/ObjectPrinting/Extensions/ObjectExtensions.cs index eba41132b..faa3dfa0f 100644 --- a/ObjectPrinting/Extensions/ObjectExtensions.cs +++ b/ObjectPrinting/Extensions/ObjectExtensions.cs @@ -17,7 +17,7 @@ public static class ObjectExtensions new DefaultObjectStrategy() }; - public static void Print(this object? obj, PrintingConfig config, + public static void Print(this object obj, PrintingConfig config, StringBuilder sb, int indent) { if (obj is null) diff --git a/ObjectPrinting/Extensions/PrintingConfigExtensions.cs b/ObjectPrinting/Extensions/PrintingConfigExtensions.cs index 203991774..7158c0f80 100644 --- a/ObjectPrinting/Extensions/PrintingConfigExtensions.cs +++ b/ObjectPrinting/Extensions/PrintingConfigExtensions.cs @@ -16,11 +16,11 @@ internal static string GetPropertyName(Expression(this PrintingConfig printingConfig, object? obj) + public static string PrintToString(this PrintingConfig printingConfig, object obj) { var sb = new StringBuilder(); printingConfig.Visited.Clear(); - obj?.Print(printingConfig, sb, 0); + obj.Print(printingConfig, sb, 0); return sb.ToString(); } } \ No newline at end of file diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index 240916516..4fab11a8b 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -10,7 +10,7 @@ public static PrintingConfig For() } /// Синтаксический сахар, вызывает сериализацию обьекта с дефолтным конфигом - public static string PrintToString(object obj) + public static string PrintToString(object? obj) { var printer = new PrintingConfig(); return printer.PrintToString((TOwner)obj); diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index 041336e19..702d9d864 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -13,7 +13,7 @@ public class PrintingConfig public HashSet ExcludedTypes { get; } = []; public HashSet ExcludedProperties { get; } = []; - public readonly HashSet Visited = []; + public readonly HashSet Visited = []; public Dictionary StringTrimmingRules { get; } = new(); public PrintingConfig Excluding() diff --git a/fluent-api.sln.DotSettings b/fluent-api.sln.DotSettings new file mode 100644 index 000000000..229f449d2 --- /dev/null +++ b/fluent-api.sln.DotSettings @@ -0,0 +1,86 @@ + + <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" 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" Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> + True + True + True + Imported 10.10.2016 + NUnitTests + NUnit Test Fixture + True + getFileNameWithoutExtension() + 1 + True + 0 + True + True + 2.0 + InCSharpTypeAndNamespace + True + 2.0 + InCSharpTypeMember + tf + True + [NUnit.Framework.TestFixture] +public class $Class$_Should +{ + [NUnit.Framework.Test] + public void $DoSomething_WhenSomething$() + { + + } +} + True + True + Imported 10.10.2016 + NUnitTests + NUnit TearDown + True + True + 2.0 + InCSharpFile + td + True + [NUnit.Framework.TearDownAttribute] +public void TearDown() +{ + $END$ +} + + True + True + Imported 10.10.2016 + NUnitTests + NUnit Test + True + 0 + True + True + 2.0 + InCSharpTypeMember + tt + True + [NUnit.Framework.Test] +public void $DoSomething_WhenSomething$() +{ + $END$ +} + True + True + Imported 10.10.2016 + NUnitTests + NUnit SetUp + True + True + 2.0 + InCSharpFile + su + True + [NUnit.Framework.SetUpAttribute] +public void SetUp() +{ + $END$ +} + \ No newline at end of file From c39a8c981ac6dd1fe209bb94df1947697891eb82 Mon Sep 17 00:00:00 2001 From: maximka200 Date: Tue, 25 Nov 2025 23:41:06 +0500 Subject: [PATCH 13/13] =?UTF-8?q?-=20=D0=B5=D1=89=D0=B5=20=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ....PrintToString_ShouldHandleListCycle_Correctly.verified.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListCycle_Correctly.verified.txt diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListCycle_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListCycle_Correctly.verified.txt new file mode 100644 index 000000000..e83c15011 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListCycle_Correctly.verified.txt @@ -0,0 +1,3 @@ +List [ + +] \ No newline at end of file