From c7425820dc7ef1daee8c915ff88ed4467de84793 Mon Sep 17 00:00:00 2001 From: Matvey Date: Wed, 19 Nov 2025 03:33:15 +0500 Subject: [PATCH 01/29] deleted solved directory and basic tests structure --- ObjectPrinting/PrintingConfig.cs | 41 ------------ 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 ---- .../Tests/ObjectPrinterAcceptanceTests.cs | 27 -------- ObjectPrinting/Tests/Person.cs | 12 ---- 10 files changed, 264 deletions(-) delete mode 100644 ObjectPrinting/PrintingConfig.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 delete mode 100644 ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs delete mode 100644 ObjectPrinting/Tests/Person.cs diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs deleted file mode 100644 index a9e082117..000000000 --- a/ObjectPrinting/PrintingConfig.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Linq; -using System.Text; - -namespace ObjectPrinting -{ - public class PrintingConfig - { - public string PrintToString(TOwner obj) - { - return PrintToString(obj, 0); - } - - private string PrintToString(object obj, int nestingLevel) - { - //TODO apply configurations - if (obj == null) - return "null" + Environment.NewLine; - - var finalTypes = new[] - { - typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan) - }; - if (finalTypes.Contains(obj.GetType())) - return obj + Environment.NewLine; - - var identation = new string('\t', nestingLevel + 1); - var sb = new StringBuilder(); - var type = obj.GetType(); - sb.AppendLine(type.Name); - foreach (var propertyInfo in type.GetProperties()) - { - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), - nestingLevel + 1)); - } - return sb.ToString(); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/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/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs deleted file mode 100644 index 4c8b2445c..000000000 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NUnit.Framework; - -namespace ObjectPrinting.Tests -{ - [TestFixture] - public class ObjectPrinterAcceptanceTests - { - [Test] - public void Demo() - { - var person = new Person { Name = "Alex", Age = 19 }; - - var printer = ObjectPrinter.For(); - //1. Исключить из сериализации свойства определенного типа - //2. Указать альтернативный способ сериализации для определенного типа - //3. Для числовых типов указать культуру - //4. Настроить сериализацию конкретного свойства - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - //6. Исключить из сериализации конкретного свойства - - string s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - //8. ...с конфигурированием - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting/Tests/Person.cs deleted file mode 100644 index f95559554..000000000 --- a/ObjectPrinting/Tests/Person.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ObjectPrinting.Tests -{ - public class Person - { - public Guid Id { get; set; } - public string Name { get; set; } - public double Height { get; set; } - public int Age { get; set; } - } -} \ No newline at end of file From 297db5e8caa02049beaaa214ce210ee30a50716d Mon Sep 17 00:00:00 2001 From: Matvey Date: Wed, 19 Nov 2025 03:34:51 +0500 Subject: [PATCH 02/29] added tests for ObjectPrinter in new project for TDD --- ...ure_WhenCultureIsSpecifiedForType_Test.txt | 7 + ...deProperty_WhenPropertyIsExcluded_Test.txt | 6 + ...uldExcludeType_WhenTypeIsExcluded_Test.txt | 6 + ...yclicReferences_WhenCycleDetected_Test.txt | 14 + ...aseIndentation_WhenObjectIsNested_Test.txt | 14 + ..._WhenDictionaryContainsPrimitives_Test.txt | 9 + ...tyObject_WhenAllPropertiesDefault_Test.txt | 7 + ...ntList_WhenListContainsPrimitives_Test.txt | 11 + ...lections_WhenCollectionsAreNested_Test.txt | 9 + ...ect_WhenObjectContainsInnerObject_Test.txt | 14 + ...Object_WhenPropertiesArePrimitive_Test.txt | 7 + ...ouldTrimStrings_WhenTrimIsApplied_Test.txt | 7 + ...WhenSerializerForPropertyProvided_Test.txt | 7 + ...zer_WhenSerializerForTypeProvided_Test.txt | 7 + ObjectPrintingTests/Models/Person.cs | 11 + .../ObjectPrinterAcceptanceTests.cs | 48 +++ ObjectPrintingTests/ObjectPrinterTests.cs | 300 ++++++++++++++++++ .../ObjectPrintingTests.csproj | 27 ++ 18 files changed, 511 insertions(+) create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldApplyCulture_WhenCultureIsSpecifiedForType_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldExcludeProperty_WhenPropertyIsExcluded_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldExcludeType_WhenTypeIsExcluded_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldHandleCyclicReferences_WhenCycleDetected_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldIncreaseIndentation_WhenObjectIsNested_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintEmptyObject_WhenAllPropertiesDefault_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintList_WhenListContainsPrimitives_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedObject_WhenObjectContainsInnerObject_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintSimpleObject_WhenPropertiesArePrimitive_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldTrimStrings_WhenTrimIsApplied_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldUseCustomPropertySerializer_WhenSerializerForPropertyProvided_Test.txt create mode 100644 ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldUseCustomTypeSerializer_WhenSerializerForTypeProvided_Test.txt create mode 100644 ObjectPrintingTests/Models/Person.cs create mode 100644 ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs create mode 100644 ObjectPrintingTests/ObjectPrinterTests.cs create mode 100644 ObjectPrintingTests/ObjectPrintingTests.csproj diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldApplyCulture_WhenCultureIsSpecifiedForType_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldApplyCulture_WhenCultureIsSpecifiedForType_Test.txt new file mode 100644 index 000000000..d9bc8c004 --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldApplyCulture_WhenCultureIsSpecifiedForType_Test.txt @@ -0,0 +1,7 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = Alex + Height = 1234,56 + Age = 10 + Friend = null + Scores = null \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldExcludeProperty_WhenPropertyIsExcluded_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldExcludeProperty_WhenPropertyIsExcluded_Test.txt new file mode 100644 index 000000000..bf419c2b0 --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldExcludeProperty_WhenPropertyIsExcluded_Test.txt @@ -0,0 +1,6 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = Alex + Height = 181 + Friend = null + Scores = null \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldExcludeType_WhenTypeIsExcluded_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldExcludeType_WhenTypeIsExcluded_Test.txt new file mode 100644 index 000000000..8c35b0a2c --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldExcludeType_WhenTypeIsExcluded_Test.txt @@ -0,0 +1,6 @@ +Person + Name = Alex + Height = 181 + Age = 30 + Friend = null + Scores = null \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldHandleCyclicReferences_WhenCycleDetected_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldHandleCyclicReferences_WhenCycleDetected_Test.txt new file mode 100644 index 000000000..9ef955cc2 --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldHandleCyclicReferences_WhenCycleDetected_Test.txt @@ -0,0 +1,14 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = A + Height = 0 + Age = 0 + Friend = + Person + Id = 00000000-0000-0000-0000-000000000000 + Name = B + Height = 0 + Age = 0 + Friend = [Cyclic Reference] + Scores = null + Scores = null \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldIncreaseIndentation_WhenObjectIsNested_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldIncreaseIndentation_WhenObjectIsNested_Test.txt new file mode 100644 index 000000000..597268bd9 --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldIncreaseIndentation_WhenObjectIsNested_Test.txt @@ -0,0 +1,14 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = Alex + Height = 180 + Age = 10 + Friend = + Person + Id = 00000000-0000-0000-0000-000000000000 + Name = John + Height = 150 + Age = 20 + Friend = null + Scores = null + Scores = null \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt new file mode 100644 index 000000000..89b632f0f --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt @@ -0,0 +1,9 @@ +Dictionary`2 + [0] = + KeyValuePair`2 + Key = one + Value = 1 + [1] = + KeyValuePair`2 + Key = two + Value = 2 \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintEmptyObject_WhenAllPropertiesDefault_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintEmptyObject_WhenAllPropertiesDefault_Test.txt new file mode 100644 index 000000000..55590cb3c --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintEmptyObject_WhenAllPropertiesDefault_Test.txt @@ -0,0 +1,7 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = null + Height = 0 + Age = 0 + Friend = null + Scores = null \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintList_WhenListContainsPrimitives_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintList_WhenListContainsPrimitives_Test.txt new file mode 100644 index 000000000..277979dea --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintList_WhenListContainsPrimitives_Test.txt @@ -0,0 +1,11 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = Test + Height = 100 + Age = 10 + Friend = null + Scores = + List`1 + [0] = 1 + [1] = 2 + [2] = 3 \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt new file mode 100644 index 000000000..d348a4524 --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt @@ -0,0 +1,9 @@ +List`1 + [0] = + List`1 + [0] = 1 + [1] = 2 + [1] = + List`1 + [0] = 3 + [1] = 4 \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedObject_WhenObjectContainsInnerObject_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedObject_WhenObjectContainsInnerObject_Test.txt new file mode 100644 index 000000000..aeb38bc70 --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedObject_WhenObjectContainsInnerObject_Test.txt @@ -0,0 +1,14 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = Alex + Height = 180 + Age = 30 + Friend = + Person + Id = 00000000-0000-0000-0000-000000000000 + Name = John + Height = 150 + Age = 20 + Friend = null + Scores = null + Scores = null \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintSimpleObject_WhenPropertiesArePrimitive_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintSimpleObject_WhenPropertiesArePrimitive_Test.txt new file mode 100644 index 000000000..a4be0ef3d --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintSimpleObject_WhenPropertiesArePrimitive_Test.txt @@ -0,0 +1,7 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = Alex + Height = 180,5 + Age = 25 + Friend = null + Scores = null \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldTrimStrings_WhenTrimIsApplied_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldTrimStrings_WhenTrimIsApplied_Test.txt new file mode 100644 index 000000000..5115a2933 --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldTrimStrings_WhenTrimIsApplied_Test.txt @@ -0,0 +1,7 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = Alex + Height = 100 + Age = 5 + Friend = null + Scores = null \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldUseCustomPropertySerializer_WhenSerializerForPropertyProvided_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldUseCustomPropertySerializer_WhenSerializerForPropertyProvided_Test.txt new file mode 100644 index 000000000..03fd1d838 --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldUseCustomPropertySerializer_WhenSerializerForPropertyProvided_Test.txt @@ -0,0 +1,7 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = NAME(Alex) + Height = 170 + Age = 18 + Friend = null + Scores = null \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldUseCustomTypeSerializer_WhenSerializerForTypeProvided_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldUseCustomTypeSerializer_WhenSerializerForTypeProvided_Test.txt new file mode 100644 index 000000000..527765ede --- /dev/null +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldUseCustomTypeSerializer_WhenSerializerForTypeProvided_Test.txt @@ -0,0 +1,7 @@ +Person + Id = 00000000-0000-0000-0000-000000000000 + Name = Alex + Height = 170 + Age = Int:18 + Friend = null + Scores = null \ No newline at end of file diff --git a/ObjectPrintingTests/Models/Person.cs b/ObjectPrintingTests/Models/Person.cs new file mode 100644 index 000000000..ca8824ef2 --- /dev/null +++ b/ObjectPrintingTests/Models/Person.cs @@ -0,0 +1,11 @@ +namespace ObjectPrintingTests.Models; + +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 Friend { get; set; } + public List Scores { get; set; } +} \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs new file mode 100644 index 000000000..bc42d3fed --- /dev/null +++ b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs @@ -0,0 +1,48 @@ +using System.Globalization; +using ObjectPrinting; +using ObjectPrinting.Configs; +using ObjectPrintingTests.Models; + +namespace ObjectPrintingTests +{ + [TestFixture] + public class ObjectPrinterAcceptanceTests + { + [Test] + public void Demo() + { + var person = new Person { Name = "Alex", Age = 19, Height = 178.2, Id = Guid.NewGuid()}; + + var printer = ObjectPrinter + .InClass() + + //1. Исключить из сериализации свойства определенного типа + .For().Exclude() + + //2. Указать альтернативный способ сериализации для определенного типа + .For().Use(d => $"{d:F1} cm") + + //3. Для числовых типов указать культуру + .For().Use(CultureInfo.InvariantCulture) + + //4. Настроить сериализацию конкретного свойства + .For(p => p.Name).Use(name => $"Current name is {name}") + + //5. Настроить обрезание строковых свойств + .For(p => p.Name).Trim(3) + + //6. Исключить конкретное свойство + .For(p => p.Age).Exclude(); + + var s1 = printer.PrintToString(person); + + //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию + var s2 = person.PrintToString(); + //8. ...с конфигурированием + var s3 = person.PrintToString(config => config + .For().Exclude() + .For(p => p.Name).Trim(2) + ); + } + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrinterTests.cs b/ObjectPrintingTests/ObjectPrinterTests.cs new file mode 100644 index 000000000..39d700ff4 --- /dev/null +++ b/ObjectPrintingTests/ObjectPrinterTests.cs @@ -0,0 +1,300 @@ +using FluentAssertions; +using ObjectPrinting; +using ObjectPrinting.Configs; +using ObjectPrintingTests.Models; + +namespace ObjectPrintingTests; + +public class ObjectPrinterTests +{ + private static string LoadExpected() => + File.ReadAllText( + $"{TestContext.CurrentContext.TestDirectory}/../../../ExpectedTestsResults/{TestContext.CurrentContext.Test.MethodName}.txt"); + + [Test] + public void ObjectPrinter_ShouldPrintNull_WhenObjectIsNull_Test() + { + var printer = ObjectPrinter.InClass(); + var result = printer.PrintToString(null); + result.Should().Be("null"); + } + + [Test] + public void ObjectPrinter_ShouldPrintSimpleObject_WhenPropertiesArePrimitive_Test() + { + var person = new Person + { + Id = Guid.Empty, + Name = "Alex", + Height = 180.5, + Age = 25, + Friend = null, + Scores = null + }; + + var printer = ObjectPrinter.InClass(); + var result = printer.PrintToString(person); + + var expected = LoadExpected(); + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldExcludeType_WhenTypeIsExcluded_Test() + { + var person = new Person + { + Id = Guid.Empty, + Name = "Alex", + Height = 181, + Age = 30, + Friend = null, + Scores = null + }; + + var printer = ObjectPrinter.InClass().For().Exclude(); + var result = printer.PrintToString(person); + + var expected = LoadExpected(); + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldExcludeProperty_WhenPropertyIsExcluded_Test() + { + var person = new Person + { + Id = Guid.Empty, + Name = "Alex", + Height = 181, + Age = 30, + Friend = null, + Scores = null + }; + + var printer = ObjectPrinter.InClass().For(p => p.Age).Exclude(); + var result = printer.PrintToString(person); + + var expected = LoadExpected(); + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldUseCustomTypeSerializer_WhenSerializerForTypeProvided_Test() + { + var person = new Person + { + Id = Guid.Empty, + Name = "Alex", + Height = 170, + Age = 18, + Friend = null, + Scores = null + }; + + var printer = ObjectPrinter.InClass() + .For().Use(i => $"Int:{i}"); + + var result = printer.PrintToString(person); + var expected = LoadExpected(); + + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldUseCustomPropertySerializer_WhenSerializerForPropertyProvided_Test() + { + var person = new Person + { + Id = Guid.Empty, + Name = "Alex", + Height = 170, + Age = 18, + Friend = null, + Scores = null + }; + + var printer = ObjectPrinter.InClass() + .For(p => p.Name).Use(v => $"NAME({v})"); + + var result = printer.PrintToString(person); + var expected = LoadExpected(); + + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldApplyCulture_WhenCultureIsSpecifiedForType_Test() + { + var person = new Person + { + Id = Guid.Empty, + Name = "Alex", + Height = 1234.56, + Age = 10, + Friend = null, + Scores = null + }; + + var printer = ObjectPrinter.InClass() + .For().Use(new System.Globalization.CultureInfo("de-DE")); + + var result = printer.PrintToString(person); + var expected = LoadExpected(); + + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldTrimStrings_WhenTrimIsApplied_Test() + { + var person = new Person + { + Id = Guid.Empty, + Name = "Alexander", + Height = 100, + Age = 5, + Friend = null, + Scores = null + }; + + var printer = ObjectPrinter.InClass() + .For(p => p.Name).Trim(4); + + var result = printer.PrintToString(person); + var expected = LoadExpected(); + + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldPrintList_WhenListContainsPrimitives_Test() + { + var person = new Person + { + Id = Guid.Empty, + Name = "Test", + Height = 100, + Age = 10, + Friend = null, + Scores = new List { 1, 2, 3 } + }; + + var printer = ObjectPrinter.InClass(); + var result = printer.PrintToString(person); + var expected = LoadExpected(); + + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldPrintNestedObject_WhenObjectContainsInnerObject_Test() + { + var friend = new Person + { + Id = Guid.Empty, + Name = "John", + Height = 150, + Age = 20 + }; + + var person = new Person + { + Id = Guid.Empty, + Name = "Alex", + Height = 180, + Age = 30, + Friend = friend + }; + + var printer = ObjectPrinter.InClass(); + var result = printer.PrintToString(person); + + var expected = LoadExpected(); + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldHandleCyclicReferences_WhenCycleDetected_Test() + { + var a = new Person { Name = "A" }; + var b = new Person { Name = "B" }; + a.Friend = b; + b.Friend = a; + + var printer = ObjectPrinter.InClass(); + var result = printer.PrintToString(a); + + var expected = LoadExpected(); + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldIncreaseIndentation_WhenObjectIsNested_Test() + { + var person = new Person + { + Id = Guid.Empty, + Name = "Alex", + Height = 180, + Age = 10, + Friend = new Person + { + Id = Guid.Empty, + Name = "John", + Height = 150, + Age = 20 + } + }; + + var printer = ObjectPrinter.InClass(); + var result = printer.PrintToString(person); + + var expected = LoadExpected(); + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldPrintEmptyObject_WhenAllPropertiesDefault_Test() + { + var person = new Person(); + var printer = ObjectPrinter.InClass(); + + var result = printer.PrintToString(person); + var expected = LoadExpected(); + + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test() + { + var dict = new Dictionary + { + ["one"] = 1, + ["two"] = 2 + }; + + var printer = ObjectPrinter.InClass>(); + var result = printer.PrintToString(dict); + + var expected = LoadExpected(); + result.Should().Be(expected); + } + + [Test] + public void ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test() + { + var data = new List> + { + new() { 1, 2 }, + new() { 3, 4 } + }; + + var printer = ObjectPrinter.InClass>>(); + var result = printer.PrintToString(data); + + var expected = LoadExpected(); + result.Should().Be(expected); + } +} \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrintingTests.csproj b/ObjectPrintingTests/ObjectPrintingTests.csproj new file mode 100644 index 000000000..94438164d --- /dev/null +++ b/ObjectPrintingTests/ObjectPrintingTests.csproj @@ -0,0 +1,27 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + From 09b3d1dfd6b2a51c12c724c9e4bff9fb2fa2807e Mon Sep 17 00:00:00 2001 From: Matvey Date: Wed, 19 Nov 2025 03:36:27 +0500 Subject: [PATCH 03/29] added PrintingSettings.cs to ObjectPrinter that will include information about serialization --- ObjectPrinting/ObjectPrinter.cs | 13 +++++++------ ObjectPrinting/PrintingSettings.cs | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 ObjectPrinting/PrintingSettings.cs diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index 3c7867c32..27792ba7a 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -1,10 +1,11 @@ -namespace ObjectPrinting +using ObjectPrinting.Configs; + +namespace ObjectPrinting; + +public class ObjectPrinter { - public class ObjectPrinter + public static PrintingConfig InClass() { - public static PrintingConfig For() - { - return new PrintingConfig(); - } + return new PrintingConfig(new PrintingSettings()); } } \ No newline at end of file diff --git a/ObjectPrinting/PrintingSettings.cs b/ObjectPrinting/PrintingSettings.cs new file mode 100644 index 000000000..fc6ef6af2 --- /dev/null +++ b/ObjectPrinting/PrintingSettings.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace ObjectPrinting; + +public class PrintingSettings +{ + public HashSet ExcludedTypes { get; } = []; + public HashSet ExcludedProperties { get; } = []; + public Dictionary TypeSerializers { get; } = new(); + public Dictionary PropertySerializers { get; } = new(); + public Dictionary TypeCultures { get; } = new(); + public Dictionary StringTrimLengths { get; } = new(); +} \ No newline at end of file From c86c148f2eae8dc688502d1ac6754f2a95246af6 Mon Sep 17 00:00:00 2001 From: Matvey Date: Wed, 19 Nov 2025 03:38:41 +0500 Subject: [PATCH 04/29] added configs that take information from method calls and put it in printing settings --- .../Configs/IChildPrintingConfig.cs | 8 +++ ObjectPrinting/Configs/PrintingConfig.cs | 26 ++++++++++ .../Configs/PropertyPrintingConfig.cs | 49 +++++++++++++++++++ ObjectPrinting/Configs/TypePrintingConfig.cs | 35 +++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 ObjectPrinting/Configs/IChildPrintingConfig.cs create mode 100644 ObjectPrinting/Configs/PrintingConfig.cs create mode 100644 ObjectPrinting/Configs/PropertyPrintingConfig.cs create mode 100644 ObjectPrinting/Configs/TypePrintingConfig.cs diff --git a/ObjectPrinting/Configs/IChildPrintingConfig.cs b/ObjectPrinting/Configs/IChildPrintingConfig.cs new file mode 100644 index 000000000..554021b6a --- /dev/null +++ b/ObjectPrinting/Configs/IChildPrintingConfig.cs @@ -0,0 +1,8 @@ +using ObjectPrinting.Configs; + +namespace ObjectPrinting; + +public interface IChildPrintingConfig +{ + PrintingConfig ParentConfig { get; } +} \ No newline at end of file diff --git a/ObjectPrinting/Configs/PrintingConfig.cs b/ObjectPrinting/Configs/PrintingConfig.cs new file mode 100644 index 000000000..e2f20a876 --- /dev/null +++ b/ObjectPrinting/Configs/PrintingConfig.cs @@ -0,0 +1,26 @@ +using System; +using System.Linq.Expressions; +using ObjectPrinting.SettingsAppliers; + +namespace ObjectPrinting.Configs; + +public class PrintingConfig(PrintingSettings settings) +{ + public PrintingSettings Settings = settings; + + public TypePrintingConfig For() + { + return new TypePrintingConfig(this); + } + + public PropertyPrintingConfig For(Expression> selector) + { + return new PropertyPrintingConfig(this, selector); + } + + public string PrintToString(TOwner obj) + { + var context = PrintingContext.Build(obj, Settings); + return PrintingFormatter.Format(context); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Configs/PropertyPrintingConfig.cs b/ObjectPrinting/Configs/PropertyPrintingConfig.cs new file mode 100644 index 000000000..8418f0a78 --- /dev/null +++ b/ObjectPrinting/Configs/PropertyPrintingConfig.cs @@ -0,0 +1,49 @@ +using System; +using System.Linq.Expressions; + +namespace ObjectPrinting.Configs; + +public class PropertyPrintingConfig : IChildPrintingConfig +{ + private readonly PrintingConfig printingConfig; + public readonly string PropertyPath; + + PrintingConfig IChildPrintingConfig.ParentConfig => printingConfig; + + internal PropertyPrintingConfig(PrintingConfig parentConfig, Expression> selector) + { + printingConfig = parentConfig ?? throw new ArgumentNullException(nameof(parentConfig)); + PropertyPath = BuildMemberPath(selector); + } + + public PrintingConfig Use(Func serializer) + { + ArgumentNullException.ThrowIfNull(serializer); + + printingConfig.Settings.PropertySerializers[PropertyPath] = serializer; + return printingConfig; + } + + public PrintingConfig Exclude() + { + printingConfig.Settings.ExcludedProperties.Add(PropertyPath); + return printingConfig; + } + + private static string BuildMemberPath(Expression> selector) + { + ArgumentNullException.ThrowIfNull(selector); + + var expression = selector.Body; + var parts = new System.Collections.Generic.List(); + while (expression is MemberExpression m) + { + parts.Add(m.Member.Name); + expression = m.Expression; + } + + parts.Reverse(); + var ownerName = typeof(TOwner).Name; + return ownerName + "." + string.Join('.', parts); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Configs/TypePrintingConfig.cs b/ObjectPrinting/Configs/TypePrintingConfig.cs new file mode 100644 index 000000000..653a37759 --- /dev/null +++ b/ObjectPrinting/Configs/TypePrintingConfig.cs @@ -0,0 +1,35 @@ +using System; + +namespace ObjectPrinting.Configs; + +public class TypePrintingConfig : IChildPrintingConfig +{ + private readonly PrintingConfig printingConfig; + + PrintingConfig IChildPrintingConfig.ParentConfig => printingConfig; + + internal TypePrintingConfig(PrintingConfig parentConfig) + { + printingConfig = parentConfig ?? throw new ArgumentNullException(nameof(parentConfig)); + } + + public PrintingConfig Use(Func serializer) + { + ArgumentNullException.ThrowIfNull(serializer); + printingConfig.Settings.TypeSerializers[typeof(TPropType)] = serializer; + return printingConfig; + } + + public PrintingConfig Use(IFormatProvider formatProvider) + { + ArgumentNullException.ThrowIfNull(formatProvider); + printingConfig.Settings.TypeCultures[typeof(TPropType)] = formatProvider; + return printingConfig; + } + + public PrintingConfig Exclude() + { + printingConfig.Settings.ExcludedTypes.Add(typeof(TPropType)); + return printingConfig; + } +} \ No newline at end of file From 7f0c50ef4f116639e12bd1612822aea33c5f8ea9 Mon Sep 17 00:00:00 2001 From: Matvey Date: Wed, 19 Nov 2025 03:39:27 +0500 Subject: [PATCH 05/29] added trim extension method to configs so only call with string type is acceptable --- .../PropertyPrintingConfigExtensions.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 ObjectPrinting/Configs/PropertyPrintingConfigExtensions.cs diff --git a/ObjectPrinting/Configs/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/Configs/PropertyPrintingConfigExtensions.cs new file mode 100644 index 000000000..8541b1303 --- /dev/null +++ b/ObjectPrinting/Configs/PropertyPrintingConfigExtensions.cs @@ -0,0 +1,22 @@ +using System; +using System.Reflection; + +namespace ObjectPrinting.Configs; + +public static class PropertyPrintingConfigExtensions +{ + public static PrintingConfig Trim( + this PropertyPrintingConfig config, + int maxLen) + { + ArgumentOutOfRangeException.ThrowIfNegative(maxLen); + + var parent = ((IChildPrintingConfig)config).ParentConfig; + + var path = config.PropertyPath; + + parent.Settings.StringTrimLengths[path] = maxLen; + + return parent; + } +} \ No newline at end of file From 0c568c7faef0cb2d6116591caa414a03f2279fd5 Mon Sep 17 00:00:00 2001 From: Matvey Date: Wed, 19 Nov 2025 03:39:57 +0500 Subject: [PATCH 06/29] added ObjectPrinter extensions --- ObjectPrinting/ObjectPrinterExtensions.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 ObjectPrinting/ObjectPrinterExtensions.cs diff --git a/ObjectPrinting/ObjectPrinterExtensions.cs b/ObjectPrinting/ObjectPrinterExtensions.cs new file mode 100644 index 000000000..6f3287e9e --- /dev/null +++ b/ObjectPrinting/ObjectPrinterExtensions.cs @@ -0,0 +1,19 @@ +using System; +using ObjectPrinting.Configs; + +namespace ObjectPrinting; + +public static class ObjectPrinterExtensions +{ + public static string PrintToString(this T obj) + { + return ObjectPrinter.InClass().PrintToString(obj); + } + + public static string PrintToString(this T obj, Func, PrintingConfig> config) + { + ArgumentNullException.ThrowIfNull(config); + var printer = config(ObjectPrinter.InClass()); + return printer.PrintToString(obj); + } +} \ No newline at end of file From b8d005c8ad30f186f401607aefa6d2920a3552e0 Mon Sep 17 00:00:00 2001 From: Matvey Date: Wed, 19 Nov 2025 03:40:49 +0500 Subject: [PATCH 07/29] added settings appliers for each possible serialization method --- .../SettingsAppliers/CultureApplier.cs | 28 +++++++++++++++++++ .../SettingsAppliers/ExcludeApplier.cs | 18 ++++++++++++ .../SettingsAppliers/ISettingsApplier.cs | 6 ++++ .../PropertySerializerApplier.cs | 23 +++++++++++++++ .../SettingsAppliers/TrimStringApplier.cs | 25 +++++++++++++++++ .../SettingsAppliers/TypeSerializerApplier.cs | 23 +++++++++++++++ 6 files changed, 123 insertions(+) create mode 100644 ObjectPrinting/SettingsAppliers/CultureApplier.cs create mode 100644 ObjectPrinting/SettingsAppliers/ExcludeApplier.cs create mode 100644 ObjectPrinting/SettingsAppliers/ISettingsApplier.cs create mode 100644 ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs create mode 100644 ObjectPrinting/SettingsAppliers/TrimStringApplier.cs create mode 100644 ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs diff --git a/ObjectPrinting/SettingsAppliers/CultureApplier.cs b/ObjectPrinting/SettingsAppliers/CultureApplier.cs new file mode 100644 index 000000000..a06bbc6cf --- /dev/null +++ b/ObjectPrinting/SettingsAppliers/CultureApplier.cs @@ -0,0 +1,28 @@ +using System; + +namespace ObjectPrinting.SettingsAppliers; + +internal class CultureApplier : ISettingsApplier +{ + public void Apply(PrintingNode root, PrintingSettings settings) + { + ApplyRecursive(root, settings); + } + + private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) + { + if (node.Type != null && settings.TypeCultures.TryGetValue(node.Type, out var culture)) + { + if (node.Value is IFormattable f) + { + node.Value = f.ToString(null, culture); + node.IsLeaf = true; + node.Children.Clear(); + return; + } + } + + foreach (var child in node.Children) + ApplyRecursive(child, settings); + } +} \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/ExcludeApplier.cs b/ObjectPrinting/SettingsAppliers/ExcludeApplier.cs new file mode 100644 index 000000000..78e1addbd --- /dev/null +++ b/ObjectPrinting/SettingsAppliers/ExcludeApplier.cs @@ -0,0 +1,18 @@ +namespace ObjectPrinting.SettingsAppliers; + +internal class ExcludeApplier : ISettingsApplier +{ + public void Apply(PrintingNode root, PrintingSettings settings) + { + ApplyRecursive(root, settings); + } + + private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) + { + node.Children.RemoveAll(c => settings.ExcludedProperties.Contains(c.Path) + || (c.Type != null && settings.ExcludedTypes.Contains(c.Type))); + + foreach (var child in node.Children) + ApplyRecursive(child, settings); + } +} \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/ISettingsApplier.cs b/ObjectPrinting/SettingsAppliers/ISettingsApplier.cs new file mode 100644 index 000000000..2c4ba524e --- /dev/null +++ b/ObjectPrinting/SettingsAppliers/ISettingsApplier.cs @@ -0,0 +1,6 @@ +namespace ObjectPrinting.SettingsAppliers; + +internal interface ISettingsApplier +{ + void Apply(PrintingNode root, PrintingSettings settings); +} \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs b/ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs new file mode 100644 index 000000000..1d25efdf5 --- /dev/null +++ b/ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs @@ -0,0 +1,23 @@ +namespace ObjectPrinting.SettingsAppliers; + +internal class PropertySerializerApplier : ISettingsApplier +{ + public void Apply(PrintingNode root, PrintingSettings settings) + { + ApplyRecursive(root, settings); + } + + private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) + { + if (settings.PropertySerializers.TryGetValue(node.Path, out var ser)) + { + node.Value = (string)ser.DynamicInvoke(node.Value)!; + node.IsLeaf = true; + node.Children.Clear(); + return; + } + + foreach (var child in node.Children) + ApplyRecursive(child, settings); + } +} \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/TrimStringApplier.cs b/ObjectPrinting/SettingsAppliers/TrimStringApplier.cs new file mode 100644 index 000000000..a37d86abe --- /dev/null +++ b/ObjectPrinting/SettingsAppliers/TrimStringApplier.cs @@ -0,0 +1,25 @@ +namespace ObjectPrinting.SettingsAppliers; + +internal class TrimStringApplier : ISettingsApplier +{ + public void Apply(PrintingNode root, PrintingSettings settings) + { + ApplyRecursive(root, settings); + } + + private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) + { + if (node.Type == typeof(string) + && node.Value is string s + && settings.StringTrimLengths.TryGetValue(node.Path, out var max)) + { + node.Value = s.Length <= max ? s : s[..max]; + node.IsLeaf = true; + node.Children.Clear(); + return; + } + + foreach (var child in node.Children) + ApplyRecursive(child, settings); + } +} \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs b/ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs new file mode 100644 index 000000000..f947c80e1 --- /dev/null +++ b/ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs @@ -0,0 +1,23 @@ +namespace ObjectPrinting.SettingsAppliers; + +internal class TypeSerializerApplier : ISettingsApplier +{ + public void Apply(PrintingNode root, PrintingSettings settings) + { + ApplyRecursive(root, settings); + } + + private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) + { + if (node.Type != null && settings.TypeSerializers.TryGetValue(node.Type, out var ser)) + { + node.Value = (string)ser.DynamicInvoke(node.Value)!; + node.IsLeaf = true; + node.Children.Clear(); + return; + } + + foreach (var child in node.Children) + ApplyRecursive(child, settings); + } +} \ No newline at end of file From 042fc39684d7249657a1ea1465fccddd3d61061a Mon Sep 17 00:00:00 2001 From: Matvey Date: Wed, 19 Nov 2025 03:42:03 +0500 Subject: [PATCH 08/29] added printing context creation that serialize all properties and types that chosen and creates tree with those properties --- ObjectPrinting/PrintingContext.cs | 105 ++++++++++++++++++++++++++++++ ObjectPrinting/PrintingNode.cs | 15 +++++ 2 files changed, 120 insertions(+) create mode 100644 ObjectPrinting/PrintingContext.cs create mode 100644 ObjectPrinting/PrintingNode.cs diff --git a/ObjectPrinting/PrintingContext.cs b/ObjectPrinting/PrintingContext.cs new file mode 100644 index 000000000..7085ad7c9 --- /dev/null +++ b/ObjectPrinting/PrintingContext.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using ObjectPrinting.SettingsAppliers; + +namespace ObjectPrinting +{ + internal static class PrintingContext + { + public static PrintingNode Build(object? root, PrintingSettings settings) + { + var visited = new HashSet(ReferenceEqualityComparer.Instance); + + var rawTree = BuildRawTree( + root, + root?.GetType().Name ?? "null", + root?.GetType().Name ?? "null", + visited + ); + + ApplySettings(rawTree, settings); + return rawTree; + } + + private static PrintingNode BuildRawTree( + object? value, + string name, + string path, + HashSet visited) + { + if (value == null) + return new PrintingNode(name, path, null, "null") { IsLeaf = true }; + + var type = value.GetType(); + + if (!type.IsValueType && visited.Contains(value)) + return new PrintingNode(name, path, type, "[Cyclic Reference]") + { + IsLeaf = true, + IsCyclic = true + }; + + if (!type.IsValueType) + visited.Add(value); + + if (type.IsPrimitive || value is decimal || value is Guid) + return Leaf(value, name, path, type); + + if (value is string s) + return Leaf(s, name, path, type); + + if (value is IEnumerable seq) + { + var node = new PrintingNode(name, path, type); + var index = 0; + + foreach (var item in seq) + { + var child = BuildRawTree( + item, + $"[{index}]", + $"{path}[{index}]", + visited + ); + node.Children.Add(child); + index++; + } + + return node; + } + + var complex = new PrintingNode(name, path, type); + + foreach (var p in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + var childPath = $"{type.Name}.{p.Name}"; + var childValue = p.GetValue(value); + + var child = BuildRawTree(childValue, p.Name, childPath, visited); + complex.Children.Add(child); + } + + return complex; + } + + private static PrintingNode Leaf(object value, string name, string path, Type type) => + new(name, path, type, value) { IsLeaf = true }; + + private static void ApplySettings(PrintingNode root, PrintingSettings settings) + { + var appliers = new ISettingsApplier[] + { + new ExcludeApplier(), + new PropertySerializerApplier(), + new TypeSerializerApplier(), + new TrimStringApplier(), + new CultureApplier() + }; + + foreach (var applier in appliers) + applier.Apply(root, settings); + } + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingNode.cs b/ObjectPrinting/PrintingNode.cs new file mode 100644 index 000000000..09b801239 --- /dev/null +++ b/ObjectPrinting/PrintingNode.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace ObjectPrinting; + +public class PrintingNode(string name, string path, Type? type, object? value = null) +{ + public string Name { get; } = name; + public string Path { get; } = path; + public Type? Type { get; } = type; + public object? Value { get; set; } = value; + public bool IsLeaf { get; set; } + public bool IsCyclic { get; set; } + public List Children { get; } = []; +} \ No newline at end of file From 35e69308fafb33c9004dd8386d3e85255572c038 Mon Sep 17 00:00:00 2001 From: Matvey Date: Wed, 19 Nov 2025 03:43:20 +0500 Subject: [PATCH 09/29] added formatter that takes the tree with properties and creates proper string with all chosen serializations and returns it to ObjectPrinter.PrintToString() --- ObjectPrinting/ObjectPrinting.csproj | 4 +- ObjectPrinting/PrintingFormatter.cs | 75 ++++++++++++++++++++++++++++ fluent-api.sln | 6 +++ 3 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 ObjectPrinting/PrintingFormatter.cs diff --git a/ObjectPrinting/ObjectPrinting.csproj b/ObjectPrinting/ObjectPrinting.csproj index c5db392ff..f8c85bb5c 100644 --- a/ObjectPrinting/ObjectPrinting.csproj +++ b/ObjectPrinting/ObjectPrinting.csproj @@ -5,8 +5,6 @@ - - - + diff --git a/ObjectPrinting/PrintingFormatter.cs b/ObjectPrinting/PrintingFormatter.cs new file mode 100644 index 000000000..99a2a3afd --- /dev/null +++ b/ObjectPrinting/PrintingFormatter.cs @@ -0,0 +1,75 @@ +using System.Text; +using ObjectPrinting.SettingsAppliers; + +namespace ObjectPrinting +{ + internal static class PrintingFormatter + { + public static string Format(PrintingNode root) + { + if (root == null) return string.Empty; + var sb = new StringBuilder(); + + if (root.IsLeaf) + { + sb.Append(root.Value); + return sb.ToString(); + } + + sb.AppendLine(root.Type?.Name ?? root.Name); + + foreach (var child in root.Children) + AppendChild(sb, child, 1); + + return sb.ToString().TrimEnd('\r', '\n'); + } + + private static void AppendChild(StringBuilder sb, PrintingNode node, int indent) + { + var indentStr = new string('\t', indent); + + if (node.IsLeaf) + { + sb.Append(indentStr) + .Append(node.Name) + .Append(" = ") + .AppendLine(node.Value?.ToString() ?? "null"); + return; + } + + sb.Append(indentStr) + .Append(node.Name) + .Append(" = ") + .AppendLine(); + + var header = node.Type?.Name ?? node.Name; + sb.Append(new string('\t', indent + 1)).AppendLine(header); + + foreach (var grand in node.Children) + { + AppendSubChild(sb, grand, indent + 2); + } + } + + private static void AppendSubChild(StringBuilder sb, PrintingNode node, int indent) + { + var indentStr = new string('\t', indent); + + if (node.IsLeaf) + { + sb.Append(indentStr) + .Append(node.Name) + .Append(" = ") + .AppendLine(node.Value?.ToString() ?? "null"); + return; + } + + sb.Append(indentStr) + .Append(node.Type?.Name ?? node.Name) + .AppendLine(); + + foreach (var child in node.Children) + AppendSubChild(sb, child, indent + 1); + } + } +} diff --git a/fluent-api.sln b/fluent-api.sln index 69c8db9ed..32e4f0fd7 100644 --- a/fluent-api.sln +++ b/fluent-api.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping.Tests", "Samp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectacle", "Samples\Spectacle\Spectacle.csproj", "{EFA9335C-411B-4597-B0B6-5438D1AE04C3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrintingTests", "ObjectPrintingTests\ObjectPrintingTests.csproj", "{D7C7A9DE-63A2-493A-BFA4-903A407FD2D4}" +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 + {D7C7A9DE-63A2-493A-BFA4-903A407FD2D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7C7A9DE-63A2-493A-BFA4-903A407FD2D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7C7A9DE-63A2-493A-BFA4-903A407FD2D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7C7A9DE-63A2-493A-BFA4-903A407FD2D4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 9cd1c4eadc52793de96ba1417f0957aa5eba1abb Mon Sep 17 00:00:00 2001 From: Matvey Date: Thu, 20 Nov 2025 03:19:38 +0500 Subject: [PATCH 10/29] little configs refactor --- .../PropertyPrintingConfigExtensions.cs | 8 +- .../{ => Interfaces}/IChildPrintingConfig.cs | 4 +- ObjectPrinting/Configs/PrintingConfig.cs | 3 +- .../Configs/PropertyPrintingConfig.cs | 1 + ObjectPrinting/Configs/TypePrintingConfig.cs | 1 + .../ObjectPrinterAcceptanceTests.cs | 79 +++++++++---------- ObjectPrintingTests/ObjectPrinterTests.cs | 1 + 7 files changed, 48 insertions(+), 49 deletions(-) rename ObjectPrinting/Configs/{ => Extensions}/PropertyPrintingConfigExtensions.cs (84%) rename ObjectPrinting/Configs/{ => Interfaces}/IChildPrintingConfig.cs (65%) diff --git a/ObjectPrinting/Configs/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/Configs/Extensions/PropertyPrintingConfigExtensions.cs similarity index 84% rename from ObjectPrinting/Configs/PropertyPrintingConfigExtensions.cs rename to ObjectPrinting/Configs/Extensions/PropertyPrintingConfigExtensions.cs index 8541b1303..0ece21feb 100644 --- a/ObjectPrinting/Configs/PropertyPrintingConfigExtensions.cs +++ b/ObjectPrinting/Configs/Extensions/PropertyPrintingConfigExtensions.cs @@ -1,7 +1,7 @@ using System; -using System.Reflection; +using ObjectPrinting.Configs.Interfaces; -namespace ObjectPrinting.Configs; +namespace ObjectPrinting.Configs.Extensions; public static class PropertyPrintingConfigExtensions { @@ -12,9 +12,9 @@ public static PrintingConfig Trim( ArgumentOutOfRangeException.ThrowIfNegative(maxLen); var parent = ((IChildPrintingConfig)config).ParentConfig; - + var path = config.PropertyPath; - + parent.Settings.StringTrimLengths[path] = maxLen; return parent; diff --git a/ObjectPrinting/Configs/IChildPrintingConfig.cs b/ObjectPrinting/Configs/Interfaces/IChildPrintingConfig.cs similarity index 65% rename from ObjectPrinting/Configs/IChildPrintingConfig.cs rename to ObjectPrinting/Configs/Interfaces/IChildPrintingConfig.cs index 554021b6a..44e7be6d1 100644 --- a/ObjectPrinting/Configs/IChildPrintingConfig.cs +++ b/ObjectPrinting/Configs/Interfaces/IChildPrintingConfig.cs @@ -1,6 +1,4 @@ -using ObjectPrinting.Configs; - -namespace ObjectPrinting; +namespace ObjectPrinting.Configs.Interfaces; public interface IChildPrintingConfig { diff --git a/ObjectPrinting/Configs/PrintingConfig.cs b/ObjectPrinting/Configs/PrintingConfig.cs index e2f20a876..a1ffa06b9 100644 --- a/ObjectPrinting/Configs/PrintingConfig.cs +++ b/ObjectPrinting/Configs/PrintingConfig.cs @@ -1,12 +1,11 @@ using System; using System.Linq.Expressions; -using ObjectPrinting.SettingsAppliers; namespace ObjectPrinting.Configs; public class PrintingConfig(PrintingSettings settings) { - public PrintingSettings Settings = settings; + public readonly PrintingSettings Settings = settings; public TypePrintingConfig For() { diff --git a/ObjectPrinting/Configs/PropertyPrintingConfig.cs b/ObjectPrinting/Configs/PropertyPrintingConfig.cs index 8418f0a78..d7adcb2e5 100644 --- a/ObjectPrinting/Configs/PropertyPrintingConfig.cs +++ b/ObjectPrinting/Configs/PropertyPrintingConfig.cs @@ -1,5 +1,6 @@ using System; using System.Linq.Expressions; +using ObjectPrinting.Configs.Interfaces; namespace ObjectPrinting.Configs; diff --git a/ObjectPrinting/Configs/TypePrintingConfig.cs b/ObjectPrinting/Configs/TypePrintingConfig.cs index 653a37759..648efba8e 100644 --- a/ObjectPrinting/Configs/TypePrintingConfig.cs +++ b/ObjectPrinting/Configs/TypePrintingConfig.cs @@ -1,4 +1,5 @@ using System; +using ObjectPrinting.Configs.Interfaces; namespace ObjectPrinting.Configs; diff --git a/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs index bc42d3fed..cdbc3f3af 100644 --- a/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs @@ -1,48 +1,47 @@ using System.Globalization; using ObjectPrinting; -using ObjectPrinting.Configs; +using ObjectPrinting.Configs.Extensions; using ObjectPrintingTests.Models; -namespace ObjectPrintingTests +namespace ObjectPrintingTests; + +[TestFixture] +public class ObjectPrinterAcceptanceTests { - [TestFixture] - public class ObjectPrinterAcceptanceTests + [Test] + public void Demo() { - [Test] - public void Demo() - { - var person = new Person { Name = "Alex", Age = 19, Height = 178.2, Id = Guid.NewGuid()}; - - var printer = ObjectPrinter - .InClass() - - //1. Исключить из сериализации свойства определенного типа - .For().Exclude() - - //2. Указать альтернативный способ сериализации для определенного типа - .For().Use(d => $"{d:F1} cm") - - //3. Для числовых типов указать культуру - .For().Use(CultureInfo.InvariantCulture) - - //4. Настроить сериализацию конкретного свойства - .For(p => p.Name).Use(name => $"Current name is {name}") - - //5. Настроить обрезание строковых свойств - .For(p => p.Name).Trim(3) - - //6. Исключить конкретное свойство - .For(p => p.Age).Exclude(); - - var s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - var s2 = person.PrintToString(); - //8. ...с конфигурированием - var s3 = person.PrintToString(config => config - .For().Exclude() - .For(p => p.Name).Trim(2) - ); - } + var person = new Person { Name = "Alex", Age = 19, Height = 178.2, Id = Guid.NewGuid() }; + + var printer = ObjectPrinter + .InClass() + + //1. Исключить из сериализации свойства определенного типа + .For().Exclude() + + //2. Указать альтернативный способ сериализации для определенного типа + .For().Use(d => $"{d:F1} cm") + + //3. Для числовых типов указать культуру + .For().Use(CultureInfo.InvariantCulture) + + //4. Настроить сериализацию конкретного свойства + .For(p => p.Name).Use(name => $"Current name is {name}") + + //5. Настроить обрезание строковых свойств + .For(p => p.Name).Trim(3) + + //6. Исключить конкретное свойство + .For(p => p.Age).Exclude(); + + var s1 = printer.PrintToString(person); + + //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию + var s2 = person.PrintToString(); + //8. ...с конфигурированием + var s3 = person.PrintToString(config => config + .For().Exclude() + .For(p => p.Name).Trim(2) + ); } } \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrinterTests.cs b/ObjectPrintingTests/ObjectPrinterTests.cs index 39d700ff4..2a1b8cfec 100644 --- a/ObjectPrintingTests/ObjectPrinterTests.cs +++ b/ObjectPrintingTests/ObjectPrinterTests.cs @@ -1,6 +1,7 @@ using FluentAssertions; using ObjectPrinting; using ObjectPrinting.Configs; +using ObjectPrinting.Configs.Extensions; using ObjectPrintingTests.Models; namespace ObjectPrintingTests; From 5b436bcdb2cb445258f6e84d2e6246c7f332d469 Mon Sep 17 00:00:00 2001 From: Matvey Date: Thu, 20 Nov 2025 03:28:03 +0500 Subject: [PATCH 11/29] got rid of DynamicInvoke with delegates --- ObjectPrinting/Configs/PropertyPrintingConfig.cs | 4 ++-- ObjectPrinting/Configs/TypePrintingConfig.cs | 2 +- ObjectPrinting/PrintingSettings.cs | 4 ++-- ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs | 2 +- ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ObjectPrinting/Configs/PropertyPrintingConfig.cs b/ObjectPrinting/Configs/PropertyPrintingConfig.cs index d7adcb2e5..520784449 100644 --- a/ObjectPrinting/Configs/PropertyPrintingConfig.cs +++ b/ObjectPrinting/Configs/PropertyPrintingConfig.cs @@ -21,10 +21,10 @@ public PrintingConfig Use(Func serializer) { ArgumentNullException.ThrowIfNull(serializer); - printingConfig.Settings.PropertySerializers[PropertyPath] = serializer; + printingConfig.Settings.PropertySerializers[PropertyPath] = obj => serializer((TPropType)obj!); return printingConfig; } - + public PrintingConfig Exclude() { printingConfig.Settings.ExcludedProperties.Add(PropertyPath); diff --git a/ObjectPrinting/Configs/TypePrintingConfig.cs b/ObjectPrinting/Configs/TypePrintingConfig.cs index 648efba8e..103b5951c 100644 --- a/ObjectPrinting/Configs/TypePrintingConfig.cs +++ b/ObjectPrinting/Configs/TypePrintingConfig.cs @@ -17,7 +17,7 @@ internal TypePrintingConfig(PrintingConfig parentConfig) public PrintingConfig Use(Func serializer) { ArgumentNullException.ThrowIfNull(serializer); - printingConfig.Settings.TypeSerializers[typeof(TPropType)] = serializer; + printingConfig.Settings.TypeSerializers[typeof(TPropType)] = obj => serializer((TPropType)obj!); return printingConfig; } diff --git a/ObjectPrinting/PrintingSettings.cs b/ObjectPrinting/PrintingSettings.cs index fc6ef6af2..89057e494 100644 --- a/ObjectPrinting/PrintingSettings.cs +++ b/ObjectPrinting/PrintingSettings.cs @@ -7,8 +7,8 @@ public class PrintingSettings { public HashSet ExcludedTypes { get; } = []; public HashSet ExcludedProperties { get; } = []; - public Dictionary TypeSerializers { get; } = new(); - public Dictionary PropertySerializers { get; } = new(); + public Dictionary> TypeSerializers { get; } = new(); + public Dictionary> PropertySerializers { get; } = new(); public Dictionary TypeCultures { get; } = new(); public Dictionary StringTrimLengths { get; } = new(); } \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs b/ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs index 1d25efdf5..13c034110 100644 --- a/ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs +++ b/ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs @@ -11,7 +11,7 @@ private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) { if (settings.PropertySerializers.TryGetValue(node.Path, out var ser)) { - node.Value = (string)ser.DynamicInvoke(node.Value)!; + node.Value = ser(node.Value); node.IsLeaf = true; node.Children.Clear(); return; diff --git a/ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs b/ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs index f947c80e1..7a7bd39c5 100644 --- a/ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs +++ b/ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs @@ -11,7 +11,7 @@ private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) { if (node.Type != null && settings.TypeSerializers.TryGetValue(node.Type, out var ser)) { - node.Value = (string)ser.DynamicInvoke(node.Value)!; + node.Value = ser(node.Value); node.IsLeaf = true; node.Children.Clear(); return; From aef709873eb6c52b4ab9490b9d7561a6a21bba57 Mon Sep 17 00:00:00 2001 From: Matvey Date: Thu, 20 Nov 2025 06:15:49 +0500 Subject: [PATCH 12/29] deleted last PrintToString implementation --- ObjectPrinting/PrintingContext.cs | 105 ------------------ ObjectPrinting/PrintingFormatter.cs | 75 ------------- ObjectPrinting/PrintingNode.cs | 15 --- .../SettingsAppliers/CultureApplier.cs | 28 ----- .../SettingsAppliers/ExcludeApplier.cs | 18 --- .../SettingsAppliers/ISettingsApplier.cs | 6 - .../PropertySerializerApplier.cs | 23 ---- .../SettingsAppliers/TrimStringApplier.cs | 25 ----- .../SettingsAppliers/TypeSerializerApplier.cs | 23 ---- 9 files changed, 318 deletions(-) delete mode 100644 ObjectPrinting/PrintingContext.cs delete mode 100644 ObjectPrinting/PrintingFormatter.cs delete mode 100644 ObjectPrinting/PrintingNode.cs delete mode 100644 ObjectPrinting/SettingsAppliers/CultureApplier.cs delete mode 100644 ObjectPrinting/SettingsAppliers/ExcludeApplier.cs delete mode 100644 ObjectPrinting/SettingsAppliers/ISettingsApplier.cs delete mode 100644 ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs delete mode 100644 ObjectPrinting/SettingsAppliers/TrimStringApplier.cs delete mode 100644 ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs diff --git a/ObjectPrinting/PrintingContext.cs b/ObjectPrinting/PrintingContext.cs deleted file mode 100644 index 7085ad7c9..000000000 --- a/ObjectPrinting/PrintingContext.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; -using ObjectPrinting.SettingsAppliers; - -namespace ObjectPrinting -{ - internal static class PrintingContext - { - public static PrintingNode Build(object? root, PrintingSettings settings) - { - var visited = new HashSet(ReferenceEqualityComparer.Instance); - - var rawTree = BuildRawTree( - root, - root?.GetType().Name ?? "null", - root?.GetType().Name ?? "null", - visited - ); - - ApplySettings(rawTree, settings); - return rawTree; - } - - private static PrintingNode BuildRawTree( - object? value, - string name, - string path, - HashSet visited) - { - if (value == null) - return new PrintingNode(name, path, null, "null") { IsLeaf = true }; - - var type = value.GetType(); - - if (!type.IsValueType && visited.Contains(value)) - return new PrintingNode(name, path, type, "[Cyclic Reference]") - { - IsLeaf = true, - IsCyclic = true - }; - - if (!type.IsValueType) - visited.Add(value); - - if (type.IsPrimitive || value is decimal || value is Guid) - return Leaf(value, name, path, type); - - if (value is string s) - return Leaf(s, name, path, type); - - if (value is IEnumerable seq) - { - var node = new PrintingNode(name, path, type); - var index = 0; - - foreach (var item in seq) - { - var child = BuildRawTree( - item, - $"[{index}]", - $"{path}[{index}]", - visited - ); - node.Children.Add(child); - index++; - } - - return node; - } - - var complex = new PrintingNode(name, path, type); - - foreach (var p in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - var childPath = $"{type.Name}.{p.Name}"; - var childValue = p.GetValue(value); - - var child = BuildRawTree(childValue, p.Name, childPath, visited); - complex.Children.Add(child); - } - - return complex; - } - - private static PrintingNode Leaf(object value, string name, string path, Type type) => - new(name, path, type, value) { IsLeaf = true }; - - private static void ApplySettings(PrintingNode root, PrintingSettings settings) - { - var appliers = new ISettingsApplier[] - { - new ExcludeApplier(), - new PropertySerializerApplier(), - new TypeSerializerApplier(), - new TrimStringApplier(), - new CultureApplier() - }; - - foreach (var applier in appliers) - applier.Apply(root, settings); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/PrintingFormatter.cs b/ObjectPrinting/PrintingFormatter.cs deleted file mode 100644 index 99a2a3afd..000000000 --- a/ObjectPrinting/PrintingFormatter.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Text; -using ObjectPrinting.SettingsAppliers; - -namespace ObjectPrinting -{ - internal static class PrintingFormatter - { - public static string Format(PrintingNode root) - { - if (root == null) return string.Empty; - var sb = new StringBuilder(); - - if (root.IsLeaf) - { - sb.Append(root.Value); - return sb.ToString(); - } - - sb.AppendLine(root.Type?.Name ?? root.Name); - - foreach (var child in root.Children) - AppendChild(sb, child, 1); - - return sb.ToString().TrimEnd('\r', '\n'); - } - - private static void AppendChild(StringBuilder sb, PrintingNode node, int indent) - { - var indentStr = new string('\t', indent); - - if (node.IsLeaf) - { - sb.Append(indentStr) - .Append(node.Name) - .Append(" = ") - .AppendLine(node.Value?.ToString() ?? "null"); - return; - } - - sb.Append(indentStr) - .Append(node.Name) - .Append(" = ") - .AppendLine(); - - var header = node.Type?.Name ?? node.Name; - sb.Append(new string('\t', indent + 1)).AppendLine(header); - - foreach (var grand in node.Children) - { - AppendSubChild(sb, grand, indent + 2); - } - } - - private static void AppendSubChild(StringBuilder sb, PrintingNode node, int indent) - { - var indentStr = new string('\t', indent); - - if (node.IsLeaf) - { - sb.Append(indentStr) - .Append(node.Name) - .Append(" = ") - .AppendLine(node.Value?.ToString() ?? "null"); - return; - } - - sb.Append(indentStr) - .Append(node.Type?.Name ?? node.Name) - .AppendLine(); - - foreach (var child in node.Children) - AppendSubChild(sb, child, indent + 1); - } - } -} diff --git a/ObjectPrinting/PrintingNode.cs b/ObjectPrinting/PrintingNode.cs deleted file mode 100644 index 09b801239..000000000 --- a/ObjectPrinting/PrintingNode.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace ObjectPrinting; - -public class PrintingNode(string name, string path, Type? type, object? value = null) -{ - public string Name { get; } = name; - public string Path { get; } = path; - public Type? Type { get; } = type; - public object? Value { get; set; } = value; - public bool IsLeaf { get; set; } - public bool IsCyclic { get; set; } - public List Children { get; } = []; -} \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/CultureApplier.cs b/ObjectPrinting/SettingsAppliers/CultureApplier.cs deleted file mode 100644 index a06bbc6cf..000000000 --- a/ObjectPrinting/SettingsAppliers/CultureApplier.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace ObjectPrinting.SettingsAppliers; - -internal class CultureApplier : ISettingsApplier -{ - public void Apply(PrintingNode root, PrintingSettings settings) - { - ApplyRecursive(root, settings); - } - - private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) - { - if (node.Type != null && settings.TypeCultures.TryGetValue(node.Type, out var culture)) - { - if (node.Value is IFormattable f) - { - node.Value = f.ToString(null, culture); - node.IsLeaf = true; - node.Children.Clear(); - return; - } - } - - foreach (var child in node.Children) - ApplyRecursive(child, settings); - } -} \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/ExcludeApplier.cs b/ObjectPrinting/SettingsAppliers/ExcludeApplier.cs deleted file mode 100644 index 78e1addbd..000000000 --- a/ObjectPrinting/SettingsAppliers/ExcludeApplier.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace ObjectPrinting.SettingsAppliers; - -internal class ExcludeApplier : ISettingsApplier -{ - public void Apply(PrintingNode root, PrintingSettings settings) - { - ApplyRecursive(root, settings); - } - - private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) - { - node.Children.RemoveAll(c => settings.ExcludedProperties.Contains(c.Path) - || (c.Type != null && settings.ExcludedTypes.Contains(c.Type))); - - foreach (var child in node.Children) - ApplyRecursive(child, settings); - } -} \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/ISettingsApplier.cs b/ObjectPrinting/SettingsAppliers/ISettingsApplier.cs deleted file mode 100644 index 2c4ba524e..000000000 --- a/ObjectPrinting/SettingsAppliers/ISettingsApplier.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace ObjectPrinting.SettingsAppliers; - -internal interface ISettingsApplier -{ - void Apply(PrintingNode root, PrintingSettings settings); -} \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs b/ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs deleted file mode 100644 index 13c034110..000000000 --- a/ObjectPrinting/SettingsAppliers/PropertySerializerApplier.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace ObjectPrinting.SettingsAppliers; - -internal class PropertySerializerApplier : ISettingsApplier -{ - public void Apply(PrintingNode root, PrintingSettings settings) - { - ApplyRecursive(root, settings); - } - - private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) - { - if (settings.PropertySerializers.TryGetValue(node.Path, out var ser)) - { - node.Value = ser(node.Value); - node.IsLeaf = true; - node.Children.Clear(); - return; - } - - foreach (var child in node.Children) - ApplyRecursive(child, settings); - } -} \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/TrimStringApplier.cs b/ObjectPrinting/SettingsAppliers/TrimStringApplier.cs deleted file mode 100644 index a37d86abe..000000000 --- a/ObjectPrinting/SettingsAppliers/TrimStringApplier.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace ObjectPrinting.SettingsAppliers; - -internal class TrimStringApplier : ISettingsApplier -{ - public void Apply(PrintingNode root, PrintingSettings settings) - { - ApplyRecursive(root, settings); - } - - private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) - { - if (node.Type == typeof(string) - && node.Value is string s - && settings.StringTrimLengths.TryGetValue(node.Path, out var max)) - { - node.Value = s.Length <= max ? s : s[..max]; - node.IsLeaf = true; - node.Children.Clear(); - return; - } - - foreach (var child in node.Children) - ApplyRecursive(child, settings); - } -} \ No newline at end of file diff --git a/ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs b/ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs deleted file mode 100644 index 7a7bd39c5..000000000 --- a/ObjectPrinting/SettingsAppliers/TypeSerializerApplier.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace ObjectPrinting.SettingsAppliers; - -internal class TypeSerializerApplier : ISettingsApplier -{ - public void Apply(PrintingNode root, PrintingSettings settings) - { - ApplyRecursive(root, settings); - } - - private static void ApplyRecursive(PrintingNode node, PrintingSettings settings) - { - if (node.Type != null && settings.TypeSerializers.TryGetValue(node.Type, out var ser)) - { - node.Value = ser(node.Value); - node.IsLeaf = true; - node.Children.Clear(); - return; - } - - foreach (var child in node.Children) - ApplyRecursive(child, settings); - } -} \ No newline at end of file From beaa56efa66ab18be86bbd41f387a8eccbf54185 Mon Sep 17 00:00:00 2001 From: Matvey Date: Thu, 20 Nov 2025 06:17:22 +0500 Subject: [PATCH 13/29] added new ValueContext for PrintToString implementation --- ObjectPrinting/PrintingHandlers/ValueContext.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 ObjectPrinting/PrintingHandlers/ValueContext.cs diff --git a/ObjectPrinting/PrintingHandlers/ValueContext.cs b/ObjectPrinting/PrintingHandlers/ValueContext.cs new file mode 100644 index 000000000..3205e9967 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/ValueContext.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace ObjectPrinting.PrintingHandlers +{ + internal sealed class ValueContext + { + public object? Value { get; set; } + public Type? Type { get; set; } + public string Path { get; init; } = ""; + public int Indent { get; init; } + public PrintingSettings Settings { get; init; } = null!; + public HashSet Visited { get; init; } = null!; + } +} \ No newline at end of file From 83d7e767d9d398e37e3e8b8a4559caf6c4f661eb Mon Sep 17 00:00:00 2001 From: Matvey Date: Thu, 20 Nov 2025 06:18:50 +0500 Subject: [PATCH 14/29] added new settings appliers without recursive applying --- .../Appliers/CultureApplier.cs | 18 ++++++++++++++++++ .../Appliers/ExcludeApplier.cs | 15 +++++++++++++++ .../Appliers/PropertySerializerApplier.cs | 13 +++++++++++++ .../Appliers/TrimStringApplier.cs | 17 +++++++++++++++++ .../Appliers/TypeSerializerApplier.cs | 15 +++++++++++++++ .../Interfaces/ISettingsApplier.cs | 6 ++++++ 6 files changed, 84 insertions(+) create mode 100644 ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureApplier.cs create mode 100644 ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/ExcludeApplier.cs create mode 100644 ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerApplier.cs create mode 100644 ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringApplier.cs create mode 100644 ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerApplier.cs create mode 100644 ObjectPrinting/PrintingHandlers/ApplyingSettings/Interfaces/ISettingsApplier.cs diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureApplier.cs new file mode 100644 index 000000000..b174dd647 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureApplier.cs @@ -0,0 +1,18 @@ +using System; +using ObjectPrinting.PrintingHandlers.ApplyingSettings.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; + +internal class CultureApplier : ISettingsApplier +{ + public ApplierResult Apply(ValueContext context) + { + if (context.Type == null || + !context.Settings.TypeCultures.TryGetValue(context.Type, out var culture) || + context.Value is not IFormattable f) + return ApplierResult.NotApplied; + + var str = f.ToString(null, culture); + return ApplierResult.SerializedValue(str); + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/ExcludeApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/ExcludeApplier.cs new file mode 100644 index 000000000..d868a7dd4 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/ExcludeApplier.cs @@ -0,0 +1,15 @@ +using ObjectPrinting.PrintingHandlers.ApplyingSettings.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; + +internal class ExcludeApplier : ISettingsApplier +{ + public ApplierResult Apply(ValueContext context) + { + if (context.Settings.ExcludedProperties.Contains(context.Path) || + (context.Type != null && context.Settings.ExcludedTypes.Contains(context.Type))) + return ApplierResult.Excluded(); + + return ApplierResult.NotApplied; + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerApplier.cs new file mode 100644 index 000000000..02fde8781 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerApplier.cs @@ -0,0 +1,13 @@ +using ObjectPrinting.PrintingHandlers.ApplyingSettings.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; + +internal class PropertySerializerApplier : ISettingsApplier +{ + public ApplierResult Apply(ValueContext context) + { + return !context.Settings.PropertySerializers.TryGetValue(context.Path, out var serializer) + ? ApplierResult.NotApplied + : ApplierResult.SerializedValue(serializer(context.Value)); + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringApplier.cs new file mode 100644 index 000000000..29e1c10f5 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringApplier.cs @@ -0,0 +1,17 @@ +using ObjectPrinting.PrintingHandlers.ApplyingSettings.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; + +internal class TrimStringApplier : ISettingsApplier +{ + public ApplierResult Apply(ValueContext context) + { + if (context.Type != typeof(string) || + context.Value is not string s || + !context.Settings.StringTrimLengths.TryGetValue(context.Path, out var max)) + return ApplierResult.NotApplied; + + var trimmed = s.Length <= max ? s : s[..max]; + return ApplierResult.Modified(trimmed); + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerApplier.cs new file mode 100644 index 000000000..a45ed920f --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerApplier.cs @@ -0,0 +1,15 @@ +using ObjectPrinting.PrintingHandlers.ApplyingSettings.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; + +internal class TypeSerializerApplier : ISettingsApplier +{ + public ApplierResult Apply(ValueContext context) + { + if (context.Type == null || + !context.Settings.TypeSerializers.TryGetValue(context.Type, out var serializer)) + return ApplierResult.NotApplied; + + return ApplierResult.SerializedValue(serializer(context.Value)); + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Interfaces/ISettingsApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Interfaces/ISettingsApplier.cs new file mode 100644 index 000000000..ce8e9ecb8 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Interfaces/ISettingsApplier.cs @@ -0,0 +1,6 @@ +namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Interfaces; + +internal interface ISettingsApplier +{ + ApplierResult Apply(ValueContext ctx); +} \ No newline at end of file From 56c3bafe6920293f5be41c33b6e348ce02069497 Mon Sep 17 00:00:00 2001 From: Matvey Date: Thu, 20 Nov 2025 06:20:07 +0500 Subject: [PATCH 15/29] added appliers results and runner for correct applying all of the settings --- .../ApplyingSettings/ApplierResult.cs | 27 ++++++++++ .../ApplyingSettings/AppliersRunner.cs | 52 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 ObjectPrinting/PrintingHandlers/ApplyingSettings/ApplierResult.cs create mode 100644 ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/ApplierResult.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/ApplierResult.cs new file mode 100644 index 000000000..2cc2a04ce --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/ApplierResult.cs @@ -0,0 +1,27 @@ +namespace ObjectPrinting.PrintingHandlers.ApplyingSettings; + +public readonly struct ApplierResult +{ + public bool Applied { get; } + public bool Exclude { get; } + public string? Serialized { get; } + public object? NewValue { get; } + + private ApplierResult(bool applied, bool exclude, string? serialized, object? newValue) + { + Applied = applied; + Exclude = exclude; + Serialized = serialized; + NewValue = newValue; + } + + public static ApplierResult NotApplied => new(false, false, null, null); + + public static ApplierResult Excluded() => new(true, true, null, null); + + public static ApplierResult SerializedValue(string value) => + new(true, false, value, null); + + public static ApplierResult Modified(object? newValue) => + new(true, false, null, newValue); +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs new file mode 100644 index 000000000..c5e23fc00 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs @@ -0,0 +1,52 @@ +using ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; +using ObjectPrinting.PrintingHandlers.ApplyingSettings.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.ApplyingSettings; + +internal class ApplierRunner +{ + private readonly ISettingsApplier[] appliers = + [ + new ExcludeApplier(), + new TrimStringApplier(), + new PropertySerializerApplier(), + new TypeSerializerApplier(), + new CultureApplier() + ]; + + public ApplierResult Run(ValueContext context) + { + var currentValue = context.Value; + var anyApplied = false; + + foreach (var applier in appliers) + { + var localContext = new ValueContext + { + Value = currentValue, + Type = currentValue?.GetType() ?? context.Type, + Path = context.Path, + Indent = context.Indent, + Settings = context.Settings, + Visited = context.Visited + }; + + var res = applier.Apply(localContext); + if (!res.Applied) + continue; + + anyApplied = true; + + if (res.Exclude) + return ApplierResult.Excluded(); + + if (res.Serialized != null) + return ApplierResult.SerializedValue(res.Serialized); + + if (res.NewValue != null) + currentValue = res.NewValue; + } + + return !anyApplied ? ApplierResult.NotApplied : ApplierResult.Modified(currentValue); + } +} \ No newline at end of file From 241850b9192b8c3634030d8836ff4890e2cc27e0 Mon Sep 17 00:00:00 2001 From: Matvey Date: Thu, 20 Nov 2025 06:21:34 +0500 Subject: [PATCH 16/29] added strategies to handle different types of objects --- .../HandlingStrategies/EnumerableStrategy.cs | 65 ++++++++++++++++ .../Interfaces/IStrategy.cs | 10 +++ .../HandlingStrategies/NullStrategy.cs | 11 +++ .../HandlingStrategies/ObjectStrategy.cs | 78 +++++++++++++++++++ .../HandlingStrategies/PrimitiveStrategy.cs | 19 +++++ .../HandlingStrategies/StrategySelector.cs | 21 +++++ .../HandlingStrategies/StringStrategy.cs | 14 ++++ 7 files changed, 218 insertions(+) create mode 100644 ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs create mode 100644 ObjectPrinting/PrintingHandlers/HandlingStrategies/Interfaces/IStrategy.cs create mode 100644 ObjectPrinting/PrintingHandlers/HandlingStrategies/NullStrategy.cs create mode 100644 ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs create mode 100644 ObjectPrinting/PrintingHandlers/HandlingStrategies/PrimitiveStrategy.cs create mode 100644 ObjectPrinting/PrintingHandlers/HandlingStrategies/StrategySelector.cs create mode 100644 ObjectPrinting/PrintingHandlers/HandlingStrategies/StringStrategy.cs diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs new file mode 100644 index 000000000..d577af359 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections; +using System.Text; +using ObjectPrinting.PrintingHandlers.HandlingStrategies.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.HandlingStrategies +{ + internal class EnumerableStrategy : IStrategy + { + public bool CanHandle(ValueContext context) + { + return context.Value switch + { + null or string => false, + _ => context.Value is IEnumerable + }; + } + + public string Print(ValueContext context, Func recurse) + { + var sequence = (IEnumerable)context.Value!; + var type = context.Type ?? sequence.GetType(); + var sb = new StringBuilder(); + sb.AppendLine($"{type.Name} ["); + + var i = 0; + foreach (var item in sequence) + { + var childContext = new ValueContext + { + Value = item, + Type = item?.GetType(), + Path = $"{context.Path}[{i}]", + Indent = context.Indent, + Settings = context.Settings, + Visited = context.Visited + }; + + var printed = recurse(childContext); + var prefix = new string('\t', context.Indent + 1); + + if (printed == "") + { + sb.Append(prefix).Append($"[{i}] = ").AppendLine("null"); + } + else if (printed.Contains(Environment.NewLine)) + { + sb.Append(prefix).Append($"[{i}] = ").AppendLine(); + var lines = printed.Split(["\r\n", "\n"], StringSplitOptions.None); + foreach (var line in lines) + sb.Append(prefix).Append('\t').AppendLine(line); + } + else + { + sb.Append(prefix).Append($"[{i}] = ").AppendLine(printed); + } + + i++; + } + + sb.Append(new string('\t', context.Indent)).Append("]"); + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/Interfaces/IStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/Interfaces/IStrategy.cs new file mode 100644 index 000000000..028ebf92c --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/Interfaces/IStrategy.cs @@ -0,0 +1,10 @@ +using System; + +namespace ObjectPrinting.PrintingHandlers.HandlingStrategies.Interfaces +{ + internal interface IStrategy + { + bool CanHandle(ValueContext context); + string Print(ValueContext context, Func recurse); + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/NullStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/NullStrategy.cs new file mode 100644 index 000000000..3a8106228 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/NullStrategy.cs @@ -0,0 +1,11 @@ +using System; +using ObjectPrinting.PrintingHandlers.HandlingStrategies.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.HandlingStrategies; + +internal class NullStrategy : IStrategy +{ + public bool CanHandle(ValueContext context) => context.Value == null; + + public string Print(ValueContext context, Func recurse) => "null"; +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs new file mode 100644 index 000000000..da1a78d94 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs @@ -0,0 +1,78 @@ +using System; +using System.Reflection; +using System.Text; +using ObjectPrinting.PrintingHandlers.HandlingStrategies.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.HandlingStrategies +{ + internal class ObjectStrategy : IStrategy + { + public bool CanHandle(ValueContext context) + { + switch (context.Value) + { + case null: + case string: + return false; + } + + var type = context.Type; + if (type == null) return true; + if (type.IsPrimitive) return false; + + return context.Value is not decimal && context.Value is not Guid; + } + + public string Print(ValueContext context, Func recurse) + { + var obj = context.Value!; + var type = context.Type ?? obj.GetType(); + + if (!type.IsValueType) + { + if (!context.Visited.Add(obj)) + return "[Cyclic Reference]"; + } + + var sb = new StringBuilder(); + sb.AppendLine(type.Name); + + var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + foreach (var p in properties) + { + var childPath = $"{type.Name}.{p.Name}"; + var value = p.GetValue(obj); + + var childContext = new ValueContext + { + Value = value, + Type = value?.GetType(), + Path = childPath, + Indent = context.Indent, + Settings = context.Settings, + Visited = context.Visited + }; + + var printed = recurse(childContext); + if (printed == "") continue; + + var prefix = new string('\t', context.Indent + 1); + if (printed.Contains(Environment.NewLine)) + { + sb.Append(prefix).Append(p.Name).Append(" = ").AppendLine(); + var childIndent = new string('\t', context.Indent + 2); + var lines = printed.Split(["\r\n", "\n"], StringSplitOptions.None); + + foreach (var line in lines) + sb.Append(childIndent).AppendLine(line); + } + else + { + sb.Append(prefix).Append(p.Name).Append(" = ").AppendLine(printed); + } + } + + return sb.ToString().TrimEnd('\r', '\n'); + } + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/PrimitiveStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/PrimitiveStrategy.cs new file mode 100644 index 000000000..4aeb852db --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/PrimitiveStrategy.cs @@ -0,0 +1,19 @@ +using System; +using ObjectPrinting.PrintingHandlers.HandlingStrategies.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.HandlingStrategies; + +internal class PrimitiveStrategy : IStrategy +{ + public bool CanHandle(ValueContext context) + { + var type = context.Type; + if (type == null) return false; + return type.IsPrimitive || context.Value is decimal || context.Value is Guid; + } + + public string Print(ValueContext context, Func recurse) + { + return context.Value?.ToString() ?? "null"; + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/StrategySelector.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/StrategySelector.cs new file mode 100644 index 000000000..b69cc2ee1 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/StrategySelector.cs @@ -0,0 +1,21 @@ +using System.Linq; +using ObjectPrinting.PrintingHandlers.HandlingStrategies.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.HandlingStrategies; + +internal class StrategySelector +{ + private readonly IStrategy[] strategies = + [ + new NullStrategy(), + new PrimitiveStrategy(), + new StringStrategy(), + new EnumerableStrategy(), + new ObjectStrategy() + ]; + + public IStrategy Select(ValueContext context) + { + return strategies.First(s => s.CanHandle(context)); + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/StringStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/StringStrategy.cs new file mode 100644 index 000000000..a041678a6 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/StringStrategy.cs @@ -0,0 +1,14 @@ +using System; +using ObjectPrinting.PrintingHandlers.HandlingStrategies.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.HandlingStrategies; + +internal class StringStrategy : IStrategy +{ + public bool CanHandle(ValueContext context) => context.Value is string; + + public string Print(ValueContext context, Func recurse) + { + return (string?)context.Value ?? "null"; + } +} \ No newline at end of file From 6b387fe92cf9f367afdaf4786b2a30504e971d10 Mon Sep 17 00:00:00 2001 From: Matvey Date: Thu, 20 Nov 2025 06:22:30 +0500 Subject: [PATCH 17/29] added new PrintingEngine.cs that works with appliers and strategies logic, added it to PrintToString in PrintingConfig.cs, new logic completely implemented --- ObjectPrinting/Configs/PrintingConfig.cs | 5 +- .../PrintingHandlers/PrintingEngine.cs | 53 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 ObjectPrinting/PrintingHandlers/PrintingEngine.cs diff --git a/ObjectPrinting/Configs/PrintingConfig.cs b/ObjectPrinting/Configs/PrintingConfig.cs index a1ffa06b9..f2c5451fc 100644 --- a/ObjectPrinting/Configs/PrintingConfig.cs +++ b/ObjectPrinting/Configs/PrintingConfig.cs @@ -1,5 +1,6 @@ using System; using System.Linq.Expressions; +using ObjectPrinting.PrintingHandlers; namespace ObjectPrinting.Configs; @@ -19,7 +20,7 @@ public PropertyPrintingConfig For(Expression(ReferenceEqualityComparer.Instance); + var context = new ValueContext + { + Value = root, + Type = root?.GetType(), + Path = root?.GetType()?.Name ?? "null", + Indent = 0, + Settings = settings, + Visited = visited + }; + + return PrintInternal(context).TrimEnd('\r', '\n'); + } + + private string PrintInternal(ValueContext context) + { + var result = applierRunner.Run(context); + + if (result.Applied) + { + if (result.Exclude) + return ""; + + if (result.Serialized != null) + return result.Serialized; + + if (result.NewValue != null) + { + context.Value = result.NewValue; + context.Type = result.NewValue?.GetType(); + } + } + + var strategy = strategySelector.Select(context); + return strategy.Print(context, PrintInternal); + } + } +} \ No newline at end of file From 1a951d26724db7b51850b2f8aaf810301d1e0d81 Mon Sep 17 00:00:00 2001 From: Matvey Date: Thu, 20 Nov 2025 06:24:39 +0500 Subject: [PATCH 18/29] added new tests, edited expected files for new more pretty enumerables serialization --- ..._WhenDictionaryContainsPrimitives_Test.txt | 5 +- ...ntList_WhenListContainsPrimitives_Test.txt | 5 +- ...lections_WhenCollectionsAreNested_Test.txt | 11 ++-- ObjectPrintingTests/ObjectPrinterTests.cs | 59 ++++++++++++++++++- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt index 89b632f0f..553bd82eb 100644 --- a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt @@ -1,4 +1,4 @@ -Dictionary`2 +Dictionary`2 [ [0] = KeyValuePair`2 Key = one @@ -6,4 +6,5 @@ Dictionary`2 [1] = KeyValuePair`2 Key = two - Value = 2 \ No newline at end of file + Value = 2 +] \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintList_WhenListContainsPrimitives_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintList_WhenListContainsPrimitives_Test.txt index 277979dea..4c928a850 100644 --- a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintList_WhenListContainsPrimitives_Test.txt +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintList_WhenListContainsPrimitives_Test.txt @@ -5,7 +5,8 @@ Person Age = 10 Friend = null Scores = - List`1 + List`1 [ [0] = 1 [1] = 2 - [2] = 3 \ No newline at end of file + [2] = 3 + ] \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt index d348a4524..573d959e9 100644 --- a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt @@ -1,9 +1,12 @@ -List`1 +List`1 [ [0] = - List`1 + List`1 [ [0] = 1 [1] = 2 + ] [1] = - List`1 + List`1 [ [0] = 3 - [1] = 4 \ No newline at end of file + [1] = 4 + ] +] \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrinterTests.cs b/ObjectPrintingTests/ObjectPrinterTests.cs index 2a1b8cfec..c34c0bcd8 100644 --- a/ObjectPrintingTests/ObjectPrinterTests.cs +++ b/ObjectPrintingTests/ObjectPrinterTests.cs @@ -1,3 +1,4 @@ +using System.Globalization; using FluentAssertions; using ObjectPrinting; using ObjectPrinting.Configs; @@ -20,6 +21,19 @@ public void ObjectPrinter_ShouldPrintNull_WhenObjectIsNull_Test() result.Should().Be("null"); } + [Test] + public void ObjectPrinter_ShouldPrintFullProperty_WhenSerializerReturnsNull_Test() + { + var person = new Person { Name = "Alex" }; + + var printer = ObjectPrinter.InClass() + .For(p => p.Name).Use(_ => null); + + var result = printer.PrintToString(person); + + result.Should().Contain("Name = Alex"); + } + [Test] public void ObjectPrinter_ShouldPrintSimpleObject_WhenPropertiesArePrimitive_Test() { @@ -138,7 +152,7 @@ public void ObjectPrinter_ShouldApplyCulture_WhenCultureIsSpecifiedForType_Test( }; var printer = ObjectPrinter.InClass() - .For().Use(new System.Globalization.CultureInfo("de-DE")); + .For().Use(new CultureInfo("de-DE")); var result = printer.PrintToString(person); var expected = LoadExpected(); @@ -298,4 +312,47 @@ public void ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_ var expected = LoadExpected(); result.Should().Be(expected); } + + [Test] + public void ObjectPrinter_ShouldFormatDateTime_WithCustomCulture_Test() + { + var date = new DateTime(2025, 5, 1, 13, 45, 0); + + var printer = ObjectPrinter.InClass() + .For().Use(new CultureInfo("de-DE")); + + var result = printer.PrintToString(date); + + result.Should().Be("01.05.2025 13:45:00"); + } + + [Test] + public void ObjectPrinter_ShouldApplyTrimBeforeSerializer_Test() + { + var person = new Person { Name = "Alexander" }; + + var printer = ObjectPrinter + .InClass() + .For(p => p.Name).Trim(4) + .For(p => p.Name).Use(s => $"<{s}>"); + + + var result = printer.PrintToString(person); + + result.Should().Contain(""); + } + + [Test] + public void ObjectPrinter_ShouldApplyCultureThenTypeSerializer_Test() + { + var person = new Person { Height = 1234.567 }; + + var printer = ObjectPrinter.InClass() + .For().Use(new CultureInfo("fr-FR")) + .For().Use(d => $"<{d}>"); + + var result = printer.PrintToString(person); + + result.Should().Contain("<1234,567>"); + } } \ No newline at end of file From e478c2d4d03789369b6806047a1ef5624fbf4974 Mon Sep 17 00:00:00 2001 From: Matvey Date: Sat, 22 Nov 2025 18:01:11 +0500 Subject: [PATCH 19/29] added trim for string type, handled it in trim applier with less priority than trim of string property --- .../TypePrintingConfigExtensions.cs | 20 +++++++++++++++++ .../Appliers/TrimStringApplier.cs | 22 ++++++++++++++----- ObjectPrinting/PrintingSettings.cs | 1 + .../ObjectPrinterAcceptanceTests.cs | 1 + 4 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 ObjectPrinting/Configs/Extensions/TypePrintingConfigExtensions.cs diff --git a/ObjectPrinting/Configs/Extensions/TypePrintingConfigExtensions.cs b/ObjectPrinting/Configs/Extensions/TypePrintingConfigExtensions.cs new file mode 100644 index 000000000..de9469dc5 --- /dev/null +++ b/ObjectPrinting/Configs/Extensions/TypePrintingConfigExtensions.cs @@ -0,0 +1,20 @@ +using System; +using ObjectPrinting.Configs.Interfaces; + +namespace ObjectPrinting.Configs.Extensions; + +public static class TypePrintingConfigExtensions +{ + public static PrintingConfig Trim( + this TypePrintingConfig config, + int maxLen) + { + ArgumentOutOfRangeException.ThrowIfNegative(maxLen); + + var parent = ((IChildPrintingConfig)config).ParentConfig; + + parent.Settings.GlobalStringTrimLength = maxLen; + + return parent; + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringApplier.cs index 29e1c10f5..2f4b6762b 100644 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringApplier.cs +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringApplier.cs @@ -6,12 +6,24 @@ internal class TrimStringApplier : ISettingsApplier { public ApplierResult Apply(ValueContext context) { - if (context.Type != typeof(string) || - context.Value is not string s || - !context.Settings.StringTrimLengths.TryGetValue(context.Path, out var max)) + if (context.Type != typeof(string) || context.Value is not string s) return ApplierResult.NotApplied; - var trimmed = s.Length <= max ? s : s[..max]; - return ApplierResult.Modified(trimmed); + var settings = context.Settings; + + if (settings.StringTrimLengths.TryGetValue(context.Path, out var propertyMax)) + { + var trimmed = s.Length <= propertyMax ? s : s[..propertyMax]; + return ApplierResult.Modified(trimmed); + } + + if (settings.GlobalStringTrimLength > 0) + { + var max = settings.GlobalStringTrimLength; + var trimmed = s.Length <= max ? s : s[..max]; + return ApplierResult.Modified(trimmed); + } + + return ApplierResult.NotApplied; } } \ No newline at end of file diff --git a/ObjectPrinting/PrintingSettings.cs b/ObjectPrinting/PrintingSettings.cs index 89057e494..39102a74f 100644 --- a/ObjectPrinting/PrintingSettings.cs +++ b/ObjectPrinting/PrintingSettings.cs @@ -11,4 +11,5 @@ public class PrintingSettings public Dictionary> PropertySerializers { get; } = new(); public Dictionary TypeCultures { get; } = new(); public Dictionary StringTrimLengths { get; } = new(); + public int GlobalStringTrimLength { get; set; } = 0; } \ No newline at end of file diff --git a/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs index cdbc3f3af..4aa134e1f 100644 --- a/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrintingTests/ObjectPrinterAcceptanceTests.cs @@ -29,6 +29,7 @@ public void Demo() .For(p => p.Name).Use(name => $"Current name is {name}") //5. Настроить обрезание строковых свойств + .For().Trim(4) .For(p => p.Name).Trim(3) //6. Исключить конкретное свойство From 80f668b6ac740d2d0142681869d733ac4fc05633 Mon Sep 17 00:00:00 2001 From: Matvey Date: Sat, 22 Nov 2025 18:25:52 +0500 Subject: [PATCH 20/29] restricted use method with culture for non-formattable types --- .../Extensions/TypePrintingConfigExtensions.cs | 16 +++++++++++++++- ObjectPrinting/Configs/TypePrintingConfig.cs | 7 ------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/ObjectPrinting/Configs/Extensions/TypePrintingConfigExtensions.cs b/ObjectPrinting/Configs/Extensions/TypePrintingConfigExtensions.cs index de9469dc5..831d8dc44 100644 --- a/ObjectPrinting/Configs/Extensions/TypePrintingConfigExtensions.cs +++ b/ObjectPrinting/Configs/Extensions/TypePrintingConfigExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using ObjectPrinting.Configs.Interfaces; namespace ObjectPrinting.Configs.Extensions; @@ -12,9 +13,22 @@ public static PrintingConfig Trim( ArgumentOutOfRangeException.ThrowIfNegative(maxLen); var parent = ((IChildPrintingConfig)config).ParentConfig; - + parent.Settings.GlobalStringTrimLength = maxLen; return parent; } + + public static PrintingConfig Use( + this TypePrintingConfig config, + IFormatProvider provider) where TPropType : IFormattable + { + ArgumentNullException.ThrowIfNull(provider); + + var parent = ((IChildPrintingConfig)config).ParentConfig; + + parent.Settings.TypeCultures[typeof(TPropType)] = provider; + + return parent; + } } \ No newline at end of file diff --git a/ObjectPrinting/Configs/TypePrintingConfig.cs b/ObjectPrinting/Configs/TypePrintingConfig.cs index 103b5951c..4ee9fc53c 100644 --- a/ObjectPrinting/Configs/TypePrintingConfig.cs +++ b/ObjectPrinting/Configs/TypePrintingConfig.cs @@ -21,13 +21,6 @@ public PrintingConfig Use(Func serializer) return printingConfig; } - public PrintingConfig Use(IFormatProvider formatProvider) - { - ArgumentNullException.ThrowIfNull(formatProvider); - printingConfig.Settings.TypeCultures[typeof(TPropType)] = formatProvider; - return printingConfig; - } - public PrintingConfig Exclude() { printingConfig.Settings.ExcludedTypes.Add(typeof(TPropType)); From 045fe7fec85d465faa17ce0d0ecdd7f8467c00f1 Mon Sep 17 00:00:00 2001 From: Matvey Date: Sat, 22 Nov 2025 18:27:12 +0500 Subject: [PATCH 21/29] renamed settings appliers --- .../{CultureApplier.cs => CultureSettingsApplier.cs} | 2 +- .../{ExcludeApplier.cs => ExcludeSettingsApplier.cs} | 2 +- ...Applier.cs => PropertySerializerSettingsApplier.cs} | 2 +- ...imStringApplier.cs => TrimStringSettingsApplier.cs} | 2 +- ...izerApplier.cs => TypeSerializerSettingsApplier.cs} | 2 +- .../ApplyingSettings/AppliersRunner.cs | 10 +++++----- 6 files changed, 10 insertions(+), 10 deletions(-) rename ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/{CultureApplier.cs => CultureSettingsApplier.cs} (90%) rename ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/{ExcludeApplier.cs => ExcludeSettingsApplier.cs} (88%) rename ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/{PropertySerializerApplier.cs => PropertySerializerSettingsApplier.cs} (85%) rename ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/{TrimStringApplier.cs => TrimStringSettingsApplier.cs} (93%) rename ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/{TypeSerializerApplier.cs => TypeSerializerSettingsApplier.cs} (87%) diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureSettingsApplier.cs similarity index 90% rename from ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureApplier.cs rename to ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureSettingsApplier.cs index b174dd647..a6082bdcc 100644 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureApplier.cs +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureSettingsApplier.cs @@ -3,7 +3,7 @@ namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; -internal class CultureApplier : ISettingsApplier +internal class CultureSettingsApplier : ISettingsApplier { public ApplierResult Apply(ValueContext context) { diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/ExcludeApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/ExcludeSettingsApplier.cs similarity index 88% rename from ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/ExcludeApplier.cs rename to ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/ExcludeSettingsApplier.cs index d868a7dd4..15ff23ccf 100644 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/ExcludeApplier.cs +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/ExcludeSettingsApplier.cs @@ -2,7 +2,7 @@ namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; -internal class ExcludeApplier : ISettingsApplier +internal class ExcludeSettingsApplier : ISettingsApplier { public ApplierResult Apply(ValueContext context) { diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerSettingsApplier.cs similarity index 85% rename from ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerApplier.cs rename to ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerSettingsApplier.cs index 02fde8781..295d08efd 100644 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerApplier.cs +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerSettingsApplier.cs @@ -2,7 +2,7 @@ namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; -internal class PropertySerializerApplier : ISettingsApplier +internal class PropertySerializerSettingsApplier : ISettingsApplier { public ApplierResult Apply(ValueContext context) { diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringSettingsApplier.cs similarity index 93% rename from ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringApplier.cs rename to ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringSettingsApplier.cs index 2f4b6762b..1957b3ed6 100644 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringApplier.cs +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringSettingsApplier.cs @@ -2,7 +2,7 @@ namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; -internal class TrimStringApplier : ISettingsApplier +internal class TrimStringSettingsApplier : ISettingsApplier { public ApplierResult Apply(ValueContext context) { diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerSettingsApplier.cs similarity index 87% rename from ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerApplier.cs rename to ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerSettingsApplier.cs index a45ed920f..39dcf7a8d 100644 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerApplier.cs +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerSettingsApplier.cs @@ -2,7 +2,7 @@ namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; -internal class TypeSerializerApplier : ISettingsApplier +internal class TypeSerializerSettingsApplier : ISettingsApplier { public ApplierResult Apply(ValueContext context) { diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs index c5e23fc00..23f6ee01f 100644 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs @@ -7,11 +7,11 @@ internal class ApplierRunner { private readonly ISettingsApplier[] appliers = [ - new ExcludeApplier(), - new TrimStringApplier(), - new PropertySerializerApplier(), - new TypeSerializerApplier(), - new CultureApplier() + new ExcludeSettingsApplier(), + new TrimStringSettingsApplier(), + new PropertySerializerSettingsApplier(), + new TypeSerializerSettingsApplier(), + new CultureSettingsApplier() ]; public ApplierResult Run(ValueContext context) From 1685801a79cd621505df2fac46ec1cd33061c39a Mon Sep 17 00:00:00 2001 From: Matvey Date: Sat, 22 Nov 2025 18:32:24 +0500 Subject: [PATCH 22/29] deleted IFormattable check after restricting culture use for non-numeric types --- .../ApplyingSettings/Appliers/CultureSettingsApplier.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureSettingsApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureSettingsApplier.cs index a6082bdcc..d5ee6f3a5 100644 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureSettingsApplier.cs +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/CultureSettingsApplier.cs @@ -8,11 +8,10 @@ internal class CultureSettingsApplier : ISettingsApplier public ApplierResult Apply(ValueContext context) { if (context.Type == null || - !context.Settings.TypeCultures.TryGetValue(context.Type, out var culture) || - context.Value is not IFormattable f) + !context.Settings.TypeCultures.TryGetValue(context.Type, out var culture)) return ApplierResult.NotApplied; - var str = f.ToString(null, culture); + var str = ((IFormattable)context.Value).ToString(null, culture); return ApplierResult.SerializedValue(str); } } \ No newline at end of file From 1b87d86177228e56bc764a22f9757a770a7815bd Mon Sep 17 00:00:00 2001 From: Matvey Date: Sat, 22 Nov 2025 18:40:08 +0500 Subject: [PATCH 23/29] merged serializer appliers for type and properties into one --- .../PropertySerializerSettingsApplier.cs | 13 ------------- .../Appliers/SerializerSettingsApplier.cs | 18 ++++++++++++++++++ .../Appliers/TypeSerializerSettingsApplier.cs | 15 --------------- .../ApplyingSettings/AppliersRunner.cs | 3 +-- 4 files changed, 19 insertions(+), 30 deletions(-) delete mode 100644 ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerSettingsApplier.cs create mode 100644 ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/SerializerSettingsApplier.cs delete mode 100644 ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerSettingsApplier.cs diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerSettingsApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerSettingsApplier.cs deleted file mode 100644 index 295d08efd..000000000 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/PropertySerializerSettingsApplier.cs +++ /dev/null @@ -1,13 +0,0 @@ -using ObjectPrinting.PrintingHandlers.ApplyingSettings.Interfaces; - -namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; - -internal class PropertySerializerSettingsApplier : ISettingsApplier -{ - public ApplierResult Apply(ValueContext context) - { - return !context.Settings.PropertySerializers.TryGetValue(context.Path, out var serializer) - ? ApplierResult.NotApplied - : ApplierResult.SerializedValue(serializer(context.Value)); - } -} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/SerializerSettingsApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/SerializerSettingsApplier.cs new file mode 100644 index 000000000..c6eb95dd8 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/SerializerSettingsApplier.cs @@ -0,0 +1,18 @@ +using ObjectPrinting.PrintingHandlers.ApplyingSettings.Interfaces; + +namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; + +internal class SerializerSettingsApplier : ISettingsApplier +{ + public ApplierResult Apply(ValueContext context) + { + if (context.Settings.PropertySerializers.TryGetValue(context.Path, out var propertySerializer)) + return ApplierResult.SerializedValue(propertySerializer(context.Value)); + + if (context.Type != null && + context.Settings.TypeSerializers.TryGetValue(context.Type, out var typeSerializer)) + return ApplierResult.SerializedValue(typeSerializer(context.Value)); + + return ApplierResult.NotApplied; + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerSettingsApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerSettingsApplier.cs deleted file mode 100644 index 39dcf7a8d..000000000 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TypeSerializerSettingsApplier.cs +++ /dev/null @@ -1,15 +0,0 @@ -using ObjectPrinting.PrintingHandlers.ApplyingSettings.Interfaces; - -namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; - -internal class TypeSerializerSettingsApplier : ISettingsApplier -{ - public ApplierResult Apply(ValueContext context) - { - if (context.Type == null || - !context.Settings.TypeSerializers.TryGetValue(context.Type, out var serializer)) - return ApplierResult.NotApplied; - - return ApplierResult.SerializedValue(serializer(context.Value)); - } -} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs index 23f6ee01f..d14c978d0 100644 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs @@ -9,8 +9,7 @@ internal class ApplierRunner [ new ExcludeSettingsApplier(), new TrimStringSettingsApplier(), - new PropertySerializerSettingsApplier(), - new TypeSerializerSettingsApplier(), + new SerializerSettingsApplier(), new CultureSettingsApplier() ]; From 0c8bb3bc9cb3bf4b9ea5cf76bae34e1d6dba1c5f Mon Sep 17 00:00:00 2001 From: Matvey Date: Sat, 22 Nov 2025 18:42:59 +0500 Subject: [PATCH 24/29] added span for string trimming --- .../Appliers/TrimStringSettingsApplier.cs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringSettingsApplier.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringSettingsApplier.cs index 1957b3ed6..9ae5091d9 100644 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringSettingsApplier.cs +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/Appliers/TrimStringSettingsApplier.cs @@ -1,3 +1,4 @@ +using System; using ObjectPrinting.PrintingHandlers.ApplyingSettings.Interfaces; namespace ObjectPrinting.PrintingHandlers.ApplyingSettings.Appliers; @@ -10,20 +11,15 @@ public ApplierResult Apply(ValueContext context) return ApplierResult.NotApplied; var settings = context.Settings; - + if (settings.StringTrimLengths.TryGetValue(context.Path, out var propertyMax)) { - var trimmed = s.Length <= propertyMax ? s : s[..propertyMax]; - return ApplierResult.Modified(trimmed); - } - - if (settings.GlobalStringTrimLength > 0) - { - var max = settings.GlobalStringTrimLength; - var trimmed = s.Length <= max ? s : s[..max]; - return ApplierResult.Modified(trimmed); + return ApplierResult.Modified(s.Length <= propertyMax ? s : s.AsSpan(0, propertyMax).ToString()); } - - return ApplierResult.NotApplied; + + if (settings.GlobalStringTrimLength <= 0) return ApplierResult.NotApplied; + + var max = settings.GlobalStringTrimLength; + return ApplierResult.Modified(s.Length <= max ? s : s.AsSpan(0, max).ToString()); } } \ No newline at end of file From 3c8850198ceef6c60d9c11fe3d4f1712855a24eb Mon Sep 17 00:00:00 2001 From: Matvey Date: Sat, 22 Nov 2025 18:52:17 +0500 Subject: [PATCH 25/29] deleted local context, editing only context.Value with appliers --- .../ApplyingSettings/AppliersRunner.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs b/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs index d14c978d0..8c28c6b32 100644 --- a/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs +++ b/ObjectPrinting/PrintingHandlers/ApplyingSettings/AppliersRunner.cs @@ -20,17 +20,9 @@ public ApplierResult Run(ValueContext context) foreach (var applier in appliers) { - var localContext = new ValueContext - { - Value = currentValue, - Type = currentValue?.GetType() ?? context.Type, - Path = context.Path, - Indent = context.Indent, - Settings = context.Settings, - Visited = context.Visited - }; - - var res = applier.Apply(localContext); + context.Value = currentValue; + + var res = applier.Apply(context); if (!res.Applied) continue; From 306a13910d4eba54f0d1fcf2d36e91b8f9c27d34 Mon Sep 17 00:00:00 2001 From: Matvey Date: Sat, 22 Nov 2025 19:00:04 +0500 Subject: [PATCH 26/29] added Enviroment.NewLine instead of \r\n and \n to split --- .../PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs | 2 +- .../PrintingHandlers/HandlingStrategies/ObjectStrategy.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs index d577af359..7f37e6975 100644 --- a/ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs @@ -46,7 +46,7 @@ public string Print(ValueContext context, Func recurse) else if (printed.Contains(Environment.NewLine)) { sb.Append(prefix).Append($"[{i}] = ").AppendLine(); - var lines = printed.Split(["\r\n", "\n"], StringSplitOptions.None); + var lines = printed.Split([Environment.NewLine], StringSplitOptions.None); foreach (var line in lines) sb.Append(prefix).Append('\t').AppendLine(line); } diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs index da1a78d94..a688d80ae 100644 --- a/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs @@ -61,7 +61,7 @@ public string Print(ValueContext context, Func recurse) { sb.Append(prefix).Append(p.Name).Append(" = ").AppendLine(); var childIndent = new string('\t', context.Indent + 2); - var lines = printed.Split(["\r\n", "\n"], StringSplitOptions.None); + var lines = printed.Split([Environment.NewLine], StringSplitOptions.None); foreach (var line in lines) sb.Append(childIndent).AppendLine(line); From fab41c79f616d0950113216e6928972d22c8529b Mon Sep 17 00:00:00 2001 From: Matvey Date: Sat, 22 Nov 2025 19:16:55 +0500 Subject: [PATCH 27/29] edited enumerable default serializing (also edited tests so new default serialize doesnt break it) --- .../HandlingStrategies/EnumerableStrategy.cs | 21 +++++-------------- ..._WhenDictionaryContainsPrimitives_Test.txt | 14 ++++++------- ...lections_WhenCollectionsAreNested_Test.txt | 18 +++++++--------- 3 files changed, 19 insertions(+), 34 deletions(-) diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs index 7f37e6975..fe71b21f1 100644 --- a/ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/EnumerableStrategy.cs @@ -31,7 +31,7 @@ public string Print(ValueContext context, Func recurse) Value = item, Type = item?.GetType(), Path = $"{context.Path}[{i}]", - Indent = context.Indent, + Indent = context.Indent + 1, Settings = context.Settings, Visited = context.Visited }; @@ -39,26 +39,15 @@ public string Print(ValueContext context, Func recurse) var printed = recurse(childContext); var prefix = new string('\t', context.Indent + 1); - if (printed == "") - { - sb.Append(prefix).Append($"[{i}] = ").AppendLine("null"); - } - else if (printed.Contains(Environment.NewLine)) - { - sb.Append(prefix).Append($"[{i}] = ").AppendLine(); - var lines = printed.Split([Environment.NewLine], StringSplitOptions.None); - foreach (var line in lines) - sb.Append(prefix).Append('\t').AppendLine(line); - } - else - { - sb.Append(prefix).Append($"[{i}] = ").AppendLine(printed); - } + sb.Append(prefix).Append($"[{i}] = "); + + sb.AppendLine(printed == "" ? "null" : printed); i++; } sb.Append(new string('\t', context.Indent)).Append("]"); + return sb.ToString(); } } diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt index 553bd82eb..2eae8158b 100644 --- a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintDictionary_WhenDictionaryContainsPrimitives_Test.txt @@ -1,10 +1,8 @@ Dictionary`2 [ - [0] = - KeyValuePair`2 - Key = one - Value = 1 - [1] = - KeyValuePair`2 - Key = two - Value = 2 + [0] = KeyValuePair`2 + Key = one + Value = 1 + [1] = KeyValuePair`2 + Key = two + Value = 2 ] \ No newline at end of file diff --git a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt index 573d959e9..99469d219 100644 --- a/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt +++ b/ObjectPrintingTests/ExpectedTestsResults/ObjectPrinter_ShouldPrintNestedCollections_WhenCollectionsAreNested_Test.txt @@ -1,12 +1,10 @@ List`1 [ - [0] = - List`1 [ - [0] = 1 - [1] = 2 - ] - [1] = - List`1 [ - [0] = 3 - [1] = 4 - ] + [0] = List`1 [ + [0] = 1 + [1] = 2 + ] + [1] = List`1 [ + [0] = 3 + [1] = 4 + ] ] \ No newline at end of file From db4f53a1ef26f65771f50e390433e40bca2fa08b Mon Sep 17 00:00:00 2001 From: Matvey Date: Sat, 22 Nov 2025 19:23:51 +0500 Subject: [PATCH 28/29] edited bad switch to if statement --- .../PrintingHandlers/HandlingStrategies/ObjectStrategy.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs index a688d80ae..35f118bc8 100644 --- a/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs @@ -9,12 +9,7 @@ internal class ObjectStrategy : IStrategy { public bool CanHandle(ValueContext context) { - switch (context.Value) - { - case null: - case string: - return false; - } + if (context.Value is null or string) return false; var type = context.Type; if (type == null) return true; From e60e2506c140f4f5ca1ea69a2072f4f4ae17c42a Mon Sep 17 00:00:00 2001 From: Matvey Date: Sat, 22 Nov 2025 19:37:55 +0500 Subject: [PATCH 29/29] move several primitive types check into helper --- .../HandlingStrategies/Helpers/TypeHelpers.cs | 12 ++++++++++++ .../HandlingStrategies/ObjectStrategy.cs | 6 ++---- .../HandlingStrategies/PrimitiveStrategy.cs | 4 ++-- 3 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 ObjectPrinting/PrintingHandlers/HandlingStrategies/Helpers/TypeHelpers.cs diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/Helpers/TypeHelpers.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/Helpers/TypeHelpers.cs new file mode 100644 index 000000000..ab47ee696 --- /dev/null +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/Helpers/TypeHelpers.cs @@ -0,0 +1,12 @@ +using System; + +namespace ObjectPrinting.PrintingHandlers.HandlingStrategies.Helpers; + +internal static class TypeHelpers +{ + public static bool IsTypePrimitive(this Type? type) + { + if (type == null) return false; + return type.IsPrimitive || type == typeof(decimal) || type == typeof(Guid); + } +} \ No newline at end of file diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs index 35f118bc8..1e53a12e0 100644 --- a/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/ObjectStrategy.cs @@ -1,6 +1,7 @@ using System; using System.Reflection; using System.Text; +using ObjectPrinting.PrintingHandlers.HandlingStrategies.Helpers; using ObjectPrinting.PrintingHandlers.HandlingStrategies.Interfaces; namespace ObjectPrinting.PrintingHandlers.HandlingStrategies @@ -12,10 +13,7 @@ public bool CanHandle(ValueContext context) if (context.Value is null or string) return false; var type = context.Type; - if (type == null) return true; - if (type.IsPrimitive) return false; - - return context.Value is not decimal && context.Value is not Guid; + return !type.IsTypePrimitive(); } public string Print(ValueContext context, Func recurse) diff --git a/ObjectPrinting/PrintingHandlers/HandlingStrategies/PrimitiveStrategy.cs b/ObjectPrinting/PrintingHandlers/HandlingStrategies/PrimitiveStrategy.cs index 4aeb852db..6b47b01eb 100644 --- a/ObjectPrinting/PrintingHandlers/HandlingStrategies/PrimitiveStrategy.cs +++ b/ObjectPrinting/PrintingHandlers/HandlingStrategies/PrimitiveStrategy.cs @@ -1,4 +1,5 @@ using System; +using ObjectPrinting.PrintingHandlers.HandlingStrategies.Helpers; using ObjectPrinting.PrintingHandlers.HandlingStrategies.Interfaces; namespace ObjectPrinting.PrintingHandlers.HandlingStrategies; @@ -8,8 +9,7 @@ internal class PrimitiveStrategy : IStrategy public bool CanHandle(ValueContext context) { var type = context.Type; - if (type == null) return false; - return type.IsPrimitive || context.Value is decimal || context.Value is Guid; + return type.IsTypePrimitive(); } public string Print(ValueContext context, Func recurse)