From 08636c258f64b62d25a4b549edfd9b063ec212d0 Mon Sep 17 00:00:00 2001 From: younggogy Date: Mon, 17 Nov 2025 20:55:05 +0500 Subject: [PATCH 01/14] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20enum=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D1=8B=20=D1=81=20=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8=20=D0=B8=20=D1=81=D1=82=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D0=B5=D0=B3=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HomeWork/RuleUtils/Dto/RuleOutcome.cs | 3 ++ .../HomeWork/RuleUtils/Dto/RuleResult.cs | 7 ++++ .../Strategies/Implementations/CultureRule.cs | 19 ++++++++++ .../Strategies/Implementations/ExcludeRule.cs | 37 +++++++++++++++++++ .../Implementations/SerializationRule.cs | 19 ++++++++++ .../Implementations/TrimStringRule.cs | 18 +++++++++ .../Interfaces/ISerializationRule.cs | 10 +++++ fluent-api.sln.DotSettings | 3 ++ 8 files changed, 116 insertions(+) create mode 100644 ObjectPrinting/HomeWork/RuleUtils/Dto/RuleOutcome.cs create mode 100644 ObjectPrinting/HomeWork/RuleUtils/Dto/RuleResult.cs create mode 100644 ObjectPrinting/HomeWork/Strategies/Implementations/CultureRule.cs create mode 100644 ObjectPrinting/HomeWork/Strategies/Implementations/ExcludeRule.cs create mode 100644 ObjectPrinting/HomeWork/Strategies/Implementations/SerializationRule.cs create mode 100644 ObjectPrinting/HomeWork/Strategies/Implementations/TrimStringRule.cs create mode 100644 ObjectPrinting/HomeWork/Strategies/Interfaces/ISerializationRule.cs diff --git a/ObjectPrinting/HomeWork/RuleUtils/Dto/RuleOutcome.cs b/ObjectPrinting/HomeWork/RuleUtils/Dto/RuleOutcome.cs new file mode 100644 index 000000000..217b72aa5 --- /dev/null +++ b/ObjectPrinting/HomeWork/RuleUtils/Dto/RuleOutcome.cs @@ -0,0 +1,3 @@ +namespace ObjectPrinting.HomeWork.RuleUtils.Dto; + +public record RuleOutcome(RuleResult Action, string? Value); \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/RuleUtils/Dto/RuleResult.cs b/ObjectPrinting/HomeWork/RuleUtils/Dto/RuleResult.cs new file mode 100644 index 000000000..d54eed608 --- /dev/null +++ b/ObjectPrinting/HomeWork/RuleUtils/Dto/RuleResult.cs @@ -0,0 +1,7 @@ +namespace ObjectPrinting.HomeWork.RuleUtils.Dto; + +public enum RuleResult +{ + Skip = 1, + Print, +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Strategies/Implementations/CultureRule.cs b/ObjectPrinting/HomeWork/Strategies/Implementations/CultureRule.cs new file mode 100644 index 000000000..2b02eed96 --- /dev/null +++ b/ObjectPrinting/HomeWork/Strategies/Implementations/CultureRule.cs @@ -0,0 +1,19 @@ +using System.Globalization; +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.Strategies.Implementations; + +public class CultureRule(CultureInfo cultureInfo) : ISerializationRule +{ + public bool CanApply(PropertyInfo propertyInfo) + { + return typeof(IFormattable).IsAssignableFrom(propertyInfo.PropertyType); + } + + public RuleOutcome Apply(object value) + { + return new RuleOutcome(RuleResult.Print, ((IFormattable)value).ToString(null, cultureInfo)); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Strategies/Implementations/ExcludeRule.cs b/ObjectPrinting/HomeWork/Strategies/Implementations/ExcludeRule.cs new file mode 100644 index 000000000..340d0bd35 --- /dev/null +++ b/ObjectPrinting/HomeWork/Strategies/Implementations/ExcludeRule.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.Strategies.Implementations; + +public class ExcludeRule : ISerializationRule +{ + private readonly Type? typeToExclude; + private readonly PropertyInfo? propertyToExclude; + + public ExcludeRule(Type type) + { + typeToExclude = type; + } + + public ExcludeRule(PropertyInfo property) + { + propertyToExclude = property; + } + + public bool CanApply(PropertyInfo propertyInfo) + { + if (propertyToExclude != null) + return propertyInfo == propertyToExclude; + + if (typeToExclude != null) + return propertyInfo.PropertyType == typeToExclude; + + return false; + } + + public RuleOutcome Apply(object value) + { + return new RuleOutcome(RuleResult.Skip, null); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Strategies/Implementations/SerializationRule.cs b/ObjectPrinting/HomeWork/Strategies/Implementations/SerializationRule.cs new file mode 100644 index 000000000..562eb9d75 --- /dev/null +++ b/ObjectPrinting/HomeWork/Strategies/Implementations/SerializationRule.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.Strategies.Implementations; + +public class SerializationRule(Func serializer, PropertyInfo? property = null) : ISerializationRule +{ + public bool CanApply(PropertyInfo propertyInfo) + { + var rightValue = propertyInfo.PropertyType is T; + return property is null ? rightValue : property == propertyInfo && rightValue; + } + + public RuleOutcome Apply(object value) + { + return new RuleOutcome(RuleResult.Print, serializer((T)value)); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Strategies/Implementations/TrimStringRule.cs b/ObjectPrinting/HomeWork/Strategies/Implementations/TrimStringRule.cs new file mode 100644 index 000000000..79919347a --- /dev/null +++ b/ObjectPrinting/HomeWork/Strategies/Implementations/TrimStringRule.cs @@ -0,0 +1,18 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.Strategies.Implementations; + +public class TrimStringRule(PropertyInfo property, int length) : ISerializationRule +{ + public bool CanApply(PropertyInfo propertyInfo) + { + return property == propertyInfo && propertyInfo.PropertyType == typeof(string); + } + + public RuleOutcome Apply(object value) + { + return new RuleOutcome(RuleResult.Print, ((string)value)[..Math.Min(length, ((string)value).Length)]); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Strategies/Interfaces/ISerializationRule.cs b/ObjectPrinting/HomeWork/Strategies/Interfaces/ISerializationRule.cs new file mode 100644 index 000000000..e2e365e2e --- /dev/null +++ b/ObjectPrinting/HomeWork/Strategies/Interfaces/ISerializationRule.cs @@ -0,0 +1,10 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; + +namespace ObjectPrinting.HomeWork.Strategies.Interfaces; + +public interface ISerializationRule +{ + bool CanApply(PropertyInfo propertyInfo); + RuleOutcome Apply(object value); +} \ No newline at end of file diff --git a/fluent-api.sln.DotSettings b/fluent-api.sln.DotSettings index 135b83ecb..53fe49b2f 100644 --- a/fluent-api.sln.DotSettings +++ b/fluent-api.sln.DotSettings @@ -1,6 +1,9 @@  <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> + True True True Imported 10.10.2016 From 8797728adbe46283230baac76feec0f037c8c0e7 Mon Sep 17 00:00:00 2001 From: younggogy Date: Mon, 17 Nov 2025 20:58:20 +0500 Subject: [PATCH 02/14] =?UTF-8?q?=D0=92=D1=8B=D0=BD=D0=B5=D1=81=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D1=83=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=86=D0=B8=D0=BA=D0=BB,=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=20?= =?UTF-8?q?=D0=B2=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5?= =?UTF-8?q?=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D1=8B=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=83=20=D1=81=20=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=D0=B0=D0=BC=D0=B8,=20=D0=BF=D1=80=D0=B8=D0=BD=D1=82?= =?UTF-8?q?=D0=BE=D0=BC=20=D0=B8=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=D0=BC=20=D1=86?= =?UTF-8?q?=D0=B8=D0=BA=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Implementations/CycleFormatter.cs | 27 +++++++ .../Implementations/PrintingProcessor.cs | 70 +++++++++++++++++++ .../PrintUtils/Interfaces/ICycleFormatter.cs | 7 ++ .../Interfaces/IPrintingProcessor.cs | 6 ++ .../Implementations/RuleProcessor.cs | 32 +++++++++ .../RuleUtils/Interfaces/IRuleProcessor.cs | 11 +++ .../Tests/ObjectPrinterAcceptanceTests.cs | 41 +++++++++++ ObjectPrinting/ObjectPrinting.csproj | 2 + ObjectPrinting/PrintingConfig.cs | 41 ----------- 9 files changed, 196 insertions(+), 41 deletions(-) create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Implementations/CycleFormatter.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Interfaces/ICycleFormatter.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Interfaces/IPrintingProcessor.cs create mode 100644 ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs create mode 100644 ObjectPrinting/HomeWork/RuleUtils/Interfaces/IRuleProcessor.cs create mode 100644 ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs delete mode 100644 ObjectPrinting/PrintingConfig.cs diff --git a/ObjectPrinting/HomeWork/PrintUtils/Implementations/CycleFormatter.cs b/ObjectPrinting/HomeWork/PrintUtils/Implementations/CycleFormatter.cs new file mode 100644 index 000000000..a1661c1c4 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Implementations/CycleFormatter.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.PrintUtils.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; + +namespace ObjectPrinting.HomeWork.PrintUtils.Implementations; + +public class CycleFormatter(IRuleProcessor ruleProcessor) : ICycleFormatter +{ + private readonly HashSet visited = []; + public string FormatReference(object obj) + { + var type = obj.GetType(); + //TODO доделать реализацию, сейчас есть ошибка в логике. Если поле хотят exclude тогда, неясно будет, в цикле, что нам указывать + var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + var basicProperty = properties.Length > 1 ? properties[1] : + properties.Length > 0 ? properties[0] : null; + var basicValue = basicProperty != null + ? ruleProcessor.ApplyRule(basicProperty.GetValue(obj), basicProperty).Value + : "?"; + return $""; + } + + public bool TryMark(object obj) + { + return visited.Add(obj); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs new file mode 100644 index 000000000..57147993d --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs @@ -0,0 +1,70 @@ +using System.Reflection; +using System.Text; +using ObjectPrinting.HomeWork.PrintUtils.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; + +namespace ObjectPrinting.HomeWork.PrintUtils.Implementations; + +public class PrintingProcessor(IRuleProcessor ruleProcessor, ICycleFormatter cycleFormatter) : IPrintingProcessor +{ + public string Print(object? obj, int nestingLevel, HashSet visited) + { + var (text, type) = HandleNullOrVisited(obj); + if (type is null) + { + return text; + } + + var sb = new StringBuilder(); + sb.AppendLine(type.Name); + + var indent = new string('\t', nestingLevel + 1); + + foreach (var prop in type.GetProperties()) + { + var value = prop.GetValue(obj); + if (value == null) continue; + + AppendProperty(sb, prop, value, nestingLevel, visited, indent); + } + + return sb.ToString().TrimEnd(); + } + + private (string? text, Type? type) HandleNullOrVisited(object? obj) + { + if (obj == null) + return ("null", null); + + var firstSeen = cycleFormatter.TryMark(obj); + if (!firstSeen) + return (cycleFormatter.FormatReference(obj), null); + + return (null, obj.GetType()); + } + + private void AppendProperty(StringBuilder sb, PropertyInfo prop, object value, int nestingLevel, + HashSet visited, string indent) + { + var typeValue = value.GetType(); + var defaultValue = typeValue.IsValueType ? Activator.CreateInstance(typeValue) : null; + if (value.Equals(defaultValue)) return; + + var ruleOutcome = ruleProcessor.ApplyRule(value, prop); + if (ruleOutcome.Action == RuleResult.Skip) return; + + sb.Append(indent + prop.Name + " = "); + + if (!(typeValue.IsPrimitive || typeValue == typeof(string)) && ruleOutcome.Value == value.ToString()) + { + sb.Append(Print(value, nestingLevel + 1, visited)); + } + else + { + sb.Append(ruleOutcome.Value); + } + + sb.Append('\n'); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Interfaces/ICycleFormatter.cs b/ObjectPrinting/HomeWork/PrintUtils/Interfaces/ICycleFormatter.cs new file mode 100644 index 000000000..80a5fb174 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Interfaces/ICycleFormatter.cs @@ -0,0 +1,7 @@ +namespace ObjectPrinting.HomeWork.PrintUtils.Interfaces; + +public interface ICycleFormatter +{ + string FormatReference(object obj); + public bool TryMark(object obj); +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Interfaces/IPrintingProcessor.cs b/ObjectPrinting/HomeWork/PrintUtils/Interfaces/IPrintingProcessor.cs new file mode 100644 index 000000000..2b31eda89 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Interfaces/IPrintingProcessor.cs @@ -0,0 +1,6 @@ +namespace ObjectPrinting.HomeWork.PrintUtils.Interfaces; + +public interface IPrintingProcessor +{ + string Print(object? obj, int nestingLevel, HashSet visited); +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs b/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs new file mode 100644 index 000000000..8495a9da7 --- /dev/null +++ b/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; +using ObjectPrinting.HomeWork.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.RuleUtils.Implementations; + +public class RuleProcessor : IRuleProcessor +{ + private readonly List rules = []; + + public void AddRule(ISerializationRule rule) + { + rules.Add(rule); + } + + public RuleOutcome ApplyRule(object propertyValue, PropertyInfo? propertyInfo) + { + foreach (var rule in rules) + { + if (propertyInfo == null || !rule.CanApply(propertyInfo)) continue; + + var result = rule.Apply(propertyValue); + if (result.Action == RuleResult.Skip) + return new RuleOutcome(RuleResult.Skip, null); + + propertyValue = result.Value; + } + + return new RuleOutcome( RuleResult.Print, propertyValue.ToString()); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/RuleUtils/Interfaces/IRuleProcessor.cs b/ObjectPrinting/HomeWork/RuleUtils/Interfaces/IRuleProcessor.cs new file mode 100644 index 000000000..7a2b3cde2 --- /dev/null +++ b/ObjectPrinting/HomeWork/RuleUtils/Interfaces/IRuleProcessor.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.RuleUtils.Interfaces; + +public interface IRuleProcessor +{ + public RuleOutcome ApplyRule(object propertyValue, PropertyInfo? propertyInfo); + public void AddRule(ISerializationRule rule); +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs new file mode 100644 index 000000000..b5620f10c --- /dev/null +++ b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs @@ -0,0 +1,41 @@ +using System.Globalization; +using FluentAssertions; +using NUnit.Framework; + +namespace ObjectPrinting.HomeWork.Tests +{ + [TestFixture] + public class ObjectPrinterAcceptanceTests + { + [Test] + public void Demo() + { + var father = new Person {Name = "Alex", Age = 44, Height = 185}; + var son = new Person{Name = "Son", Age = 2, Parent = father}; + father.Child = son; + + var printer = ObjectPrinter + .For() + .Exclude() + .SetNumericCulture(CultureInfo.InvariantCulture) + .SerializeType(x=>$"Возраст: {x}") + .SerializeProperty(x=>x.Name, x=>$"Имя: {x}") + .Trim(x=>x.Name, 2) + .Exclude(x=>x.Height); + //1. Исключить из сериализации свойства определенного типа + //2. Указать альтернативный способ сериализации для определенного типа + //3. Для числовых типов указать культуру + //4. Настроить сериализацию конкретного свойства + //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) + //6. Исключить из сериализации конкретного свойства + + var actual = printer.PrintToString(father); + var expected = + "Person\r\n\tName = Al\n\tChild = Person\r\n\t\tName = So\n\t\tParent = "; + actual.Should().Be(expected); + + //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию + //8. ...с конфигурированием + } + } +} \ No newline at end of file diff --git a/ObjectPrinting/ObjectPrinting.csproj b/ObjectPrinting/ObjectPrinting.csproj index c5db392ff..986726d73 100644 --- a/ObjectPrinting/ObjectPrinting.csproj +++ b/ObjectPrinting/ObjectPrinting.csproj @@ -2,9 +2,11 @@ net8.0 enable + enable + 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 From f55388d208ec56675cf434b295eef481a3929517 Mon Sep 17 00:00:00 2001 From: younggogy Date: Mon, 17 Nov 2025 20:59:09 +0500 Subject: [PATCH 03/14] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D1=81=20=D0=B2=D1=81=D0=B5=20=D0=B2=20=D0=BF=D0=B0=D0=BF=D0=BA?= =?UTF-8?q?=D1=83=20Homework,=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8=D0=BB?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BD=D0=B5=D1=87=D0=BD=D1=8B=D0=B9=20PrintingC?= =?UTF-8?q?onfig.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/HomeWork/ObjectPrinter.cs | 16 +++++ .../HomeWork/PrintUtils/PrintingConfig.cs | 65 +++++++++++++++++++ ObjectPrinting/{ => HomeWork}/Tests/Person.cs | 6 +- ObjectPrinting/ObjectPrinter.cs | 10 --- .../Tests/ObjectPrinterAcceptanceTests.cs | 27 -------- 5 files changed, 84 insertions(+), 40 deletions(-) create mode 100644 ObjectPrinting/HomeWork/ObjectPrinter.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs rename ObjectPrinting/{ => HomeWork}/Tests/Person.cs (60%) delete mode 100644 ObjectPrinting/ObjectPrinter.cs delete mode 100644 ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs diff --git a/ObjectPrinting/HomeWork/ObjectPrinter.cs b/ObjectPrinting/HomeWork/ObjectPrinter.cs new file mode 100644 index 000000000..8ef39476c --- /dev/null +++ b/ObjectPrinting/HomeWork/ObjectPrinter.cs @@ -0,0 +1,16 @@ +using ObjectPrinting.HomeWork.PrintUtils; +using ObjectPrinting.HomeWork.PrintUtils.Implementations; +using ObjectPrinting.HomeWork.PrintUtils.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Implementations; + +namespace ObjectPrinting.HomeWork; + +public class ObjectPrinter +{ + public static PrintingConfig For() + { + var ruleProcessor = new RuleProcessor(); + var cycleForammter = new CycleFormatter(ruleProcessor); + return new PrintingConfig(ruleProcessor, new PrintingProcessor(ruleProcessor, cycleForammter)); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs new file mode 100644 index 000000000..c797015f3 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs @@ -0,0 +1,65 @@ +using System.Globalization; +using System.Linq.Expressions; +using System.Reflection; +using ObjectPrinting.HomeWork.PrintUtils.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; +using ObjectPrinting.HomeWork.Strategies.Implementations; + +namespace ObjectPrinting.HomeWork.PrintUtils; + +public class PrintingConfig(IRuleProcessor ruleProcessor, IPrintingProcessor printingProcessor) +{ + public string PrintToString(TOwner obj) + { + return printingProcessor.Print(obj, 0, []); + } + + public PrintingConfig Exclude() + { + var type = typeof(T); + ruleProcessor.AddRule(new ExcludeRule(type)); + return this; + } + + public PrintingConfig SetNumericCulture(CultureInfo culture) + { + ruleProcessor.AddRule(new CultureRule(culture)); + return this; + } + + public PrintingConfig SerializeType(Func serializer) + { + ruleProcessor.AddRule(new SerializationRule(serializer)); + return this; + } + + public PrintingConfig SerializeProperty( + Expression> property, Func serializer) + { + var body = property.Body as MemberExpression; + var propertyInfo = body?.Member as PropertyInfo; + ruleProcessor.AddRule(new SerializationRule(serializer, propertyInfo)); + return this; + } + + public PrintingConfig Trim(Expression> property, int length) + { + var body = property.Body as MemberExpression; + var propertyInfo = body?.Member as PropertyInfo; + if (propertyInfo is null) + { + throw new NullReferenceException(); + } + + ruleProcessor.AddRule(new TrimStringRule(propertyInfo, length)); + return this; + } + + public PrintingConfig Exclude(Expression> property) + { + var body = property.Body as MemberExpression; + var propertyInfo = body?.Member as PropertyInfo; + ruleProcessor.AddRule(new ExcludeRule(propertyInfo)); + return this; + } +} \ No newline at end of file diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting/HomeWork/Tests/Person.cs similarity index 60% rename from ObjectPrinting/Tests/Person.cs rename to ObjectPrinting/HomeWork/Tests/Person.cs index f95559554..d18e23f23 100644 --- a/ObjectPrinting/Tests/Person.cs +++ b/ObjectPrinting/HomeWork/Tests/Person.cs @@ -1,6 +1,4 @@ -using System; - -namespace ObjectPrinting.Tests +namespace ObjectPrinting.HomeWork.Tests { public class Person { @@ -8,5 +6,7 @@ public class Person public string Name { get; set; } public double Height { get; set; } public int Age { get; set; } + public Person Parent { get; set; } + public Person Child { get; set; } } } \ No newline at end of file diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs deleted file mode 100644 index 3c7867c32..000000000 --- a/ObjectPrinting/ObjectPrinter.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ObjectPrinting -{ - public class ObjectPrinter - { - public static PrintingConfig For() - { - return new PrintingConfig(); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/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 From 3a9eb7c8f9de542979aeb7f15cbbe0f1a79a9e10 Mon Sep 17 00:00:00 2001 From: younggogy Date: Mon, 17 Nov 2025 21:42:12 +0500 Subject: [PATCH 04/14] =?UTF-8?q?=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D1=83=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D1=81=D0=B5=D1=80=D0=B8=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs | 4 ++-- ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs index c797015f3..4ea554999 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs @@ -27,13 +27,13 @@ public PrintingConfig SetNumericCulture(CultureInfo culture) return this; } - public PrintingConfig SerializeType(Func serializer) + public PrintingConfig Serialize(Func serializer) { ruleProcessor.AddRule(new SerializationRule(serializer)); return this; } - public PrintingConfig SerializeProperty( + public PrintingConfig Serialize( Expression> property, Func serializer) { var body = property.Body as MemberExpression; diff --git a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs index b5620f10c..3c3c90551 100644 --- a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs @@ -18,8 +18,8 @@ public void Demo() .For() .Exclude() .SetNumericCulture(CultureInfo.InvariantCulture) - .SerializeType(x=>$"Возраст: {x}") - .SerializeProperty(x=>x.Name, x=>$"Имя: {x}") + .Serialize(x=>$"Возраст: {x}") + .Serialize(x=>x.Name, x=>$"Имя: {x}") .Trim(x=>x.Name, 2) .Exclude(x=>x.Height); //1. Исключить из сериализации свойства определенного типа From c7e4a10c65292ecc4631343eea1cbbcd9b1dbeb1 Mon Sep 17 00:00:00 2001 From: younggogy Date: Thu, 20 Nov 2025 00:16:42 +0500 Subject: [PATCH 05/14] =?UTF-8?q?=D0=9F=D0=B0=D1=80=D1=83=20=D1=84=D0=B8?= =?UTF-8?q?=D0=BA=D1=81=D0=BE=D0=B2=20+=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Implementations/RuleProcessor.cs | 26 +++++++++++++------ .../Strategies/Implementations/CultureRule.cs | 0 .../Strategies/Implementations/ExcludeRule.cs | 0 .../Implementations/SerializationRule.cs | 10 +++++-- .../Implementations/TrimStringRule.cs | 0 .../Interfaces/ISerializationRule.cs | 0 6 files changed, 26 insertions(+), 10 deletions(-) rename ObjectPrinting/HomeWork/{ => RuleUtils}/Strategies/Implementations/CultureRule.cs (100%) rename ObjectPrinting/HomeWork/{ => RuleUtils}/Strategies/Implementations/ExcludeRule.cs (100%) rename ObjectPrinting/HomeWork/{ => RuleUtils}/Strategies/Implementations/SerializationRule.cs (61%) rename ObjectPrinting/HomeWork/{ => RuleUtils}/Strategies/Implementations/TrimStringRule.cs (100%) rename ObjectPrinting/HomeWork/{ => RuleUtils}/Strategies/Interfaces/ISerializationRule.cs (100%) diff --git a/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs b/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs index 8495a9da7..25c270d52 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs @@ -13,20 +13,30 @@ public void AddRule(ISerializationRule rule) { rules.Add(rule); } - - public RuleOutcome ApplyRule(object propertyValue, PropertyInfo? propertyInfo) + + public RuleOutcome ApplyRule(object? propertyValue, PropertyInfo? propertyInfo) { - foreach (var rule in rules) + if (propertyValue == null) + return new RuleOutcome(RuleResult.Print, "null"); + + var current = propertyValue; + string? resultString = null; + + foreach (var rule in rules.Where(r => r.CanApply(propertyInfo))) { - if (propertyInfo == null || !rule.CanApply(propertyInfo)) continue; + var outcome = rule.Apply(current); - var result = rule.Apply(propertyValue); - if (result.Action == RuleResult.Skip) + if (outcome.Action == RuleResult.Skip) return new RuleOutcome(RuleResult.Skip, null); - propertyValue = result.Value; + current = outcome.Value ?? current; + resultString = outcome.Value; } - return new RuleOutcome( RuleResult.Print, propertyValue.ToString()); + + return new RuleOutcome( + RuleResult.Print, + resultString ?? propertyValue.ToString() + ); } } \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Strategies/Implementations/CultureRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs similarity index 100% rename from ObjectPrinting/HomeWork/Strategies/Implementations/CultureRule.cs rename to ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs diff --git a/ObjectPrinting/HomeWork/Strategies/Implementations/ExcludeRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs similarity index 100% rename from ObjectPrinting/HomeWork/Strategies/Implementations/ExcludeRule.cs rename to ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs diff --git a/ObjectPrinting/HomeWork/Strategies/Implementations/SerializationRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs similarity index 61% rename from ObjectPrinting/HomeWork/Strategies/Implementations/SerializationRule.cs rename to ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs index 562eb9d75..346ce7f30 100644 --- a/ObjectPrinting/HomeWork/Strategies/Implementations/SerializationRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs @@ -8,12 +8,18 @@ public class SerializationRule(Func serializer, PropertyInfo? prop { public bool CanApply(PropertyInfo propertyInfo) { - var rightValue = propertyInfo.PropertyType is T; + if (propertyInfo is null) return true; + var rightValue = propertyInfo.PropertyType == typeof(T); return property is null ? rightValue : property == propertyInfo && rightValue; } public RuleOutcome Apply(object value) { - return new RuleOutcome(RuleResult.Print, serializer((T)value)); + if (value is T typedValue) + { + return new RuleOutcome(RuleResult.Print, serializer(typedValue)); + } + + return new RuleOutcome(RuleResult.Print, value.ToString()); } } \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Strategies/Implementations/TrimStringRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs similarity index 100% rename from ObjectPrinting/HomeWork/Strategies/Implementations/TrimStringRule.cs rename to ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs diff --git a/ObjectPrinting/HomeWork/Strategies/Interfaces/ISerializationRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs similarity index 100% rename from ObjectPrinting/HomeWork/Strategies/Interfaces/ISerializationRule.cs rename to ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs From aa52874ed30b3364ce3d0c484f18074a6f235a3b Mon Sep 17 00:00:00 2001 From: younggogy Date: Thu, 20 Nov 2025 00:17:13 +0500 Subject: [PATCH 06/14] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20=D1=82=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Tests/ObjectPrinterAcceptanceTests.cs | 411 ++++++++++++++++-- ObjectPrinting/HomeWork/Tests/Person.cs | 8 +- ObjectPrinting/HomeWork/Tests/TestSpec.cs | 15 + 3 files changed, 402 insertions(+), 32 deletions(-) create mode 100644 ObjectPrinting/HomeWork/Tests/TestSpec.cs diff --git a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs index 3c3c90551..3137a49c8 100644 --- a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs @@ -1,41 +1,392 @@ -using System.Globalization; +using System; +using System.Collections.Generic; +using System.Globalization; using FluentAssertions; using NUnit.Framework; +using ObjectPrinting.HomeWork.Extensions; namespace ObjectPrinting.HomeWork.Tests { [TestFixture] - public class ObjectPrinterAcceptanceTests + public class ObjectPrinterGeneratedTests { - [Test] - public void Demo() + private static readonly int[] execute = [1,2,3]; + + [TestCaseSource(nameof(TestCases))] + public void GeneratedTestRunner(TestSpec spec) + { + var result = spec.Execute(); + + foreach (var expected in spec.ShouldContain) + { + result.Should().Contain(expected, + $"Expected output to contain '{expected}' for scenario: {spec.Description}"); + } + + foreach (var notExpected in spec.ShouldNotContain) + { + result.Should().NotContain(notExpected, + $"Expected output NOT to contain '{notExpected}' for scenario: {spec.Description}"); + } + } + + public static IEnumerable TestCases { - var father = new Person {Name = "Alex", Age = 44, Height = 185}; - var son = new Person{Name = "Son", Age = 2, Parent = father}; - father.Child = son; - - var printer = ObjectPrinter - .For() - .Exclude() - .SetNumericCulture(CultureInfo.InvariantCulture) - .Serialize(x=>$"Возраст: {x}") - .Serialize(x=>x.Name, x=>$"Имя: {x}") - .Trim(x=>x.Name, 2) - .Exclude(x=>x.Height); - //1. Исключить из сериализации свойства определенного типа - //2. Указать альтернативный способ сериализации для определенного типа - //3. Для числовых типов указать культуру - //4. Настроить сериализацию конкретного свойства - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - //6. Исключить из сериализации конкретного свойства - - var actual = printer.PrintToString(father); - var expected = - "Person\r\n\tName = Al\n\tChild = Person\r\n\t\tName = So\n\t\tParent = "; - actual.Should().Be(expected); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - //8. ...с конфигурированием + get + { + yield return new TestCaseData(new TestSpec( + "Exclude should remove int fields", + () => + { + var p = new Person { Name = "A", Age = 10 }; + var pr = ClassPrinter.For().Exclude(); + return pr.PrintToString(p); + }, + shouldNotContain: ["10"] + )).SetName("ShouldBeTrue_When_ExcludeType_RemovesAllFieldsOfThatType"); + + yield return new TestCaseData(new TestSpec( + "Exclude specific property (Height) should remove it", + () => + { + var p = new Person { Height = 180 }; + var pr = ClassPrinter.For().Exclude(x => x.Height); + return pr.PrintToString(p); + }, + shouldNotContain: ["180"] + )).SetName("ShouldBeTrue_When_ExcludeSpecificProperty_RemovesIt"); + + yield return new TestCaseData(new TestSpec( + "Serialize type int should apply custom serializer", + () => + { + var p = new Person { Age = 5 }; + var pr = ClassPrinter.For().Serialize(x => $"[{x}]"); + return pr.PrintToString(p); + }, + shouldContain: ["[5]"] + )).SetName("ShouldBeTrue_When_SerializeForType_AppliesCustomSerializer"); + + yield return new TestCaseData(new TestSpec( + "Serialize specific property Name should apply custom serializer", + () => + { + var p = new Person { Name = "Bob" }; + var pr = ClassPrinter.For().Serialize(x => x.Name, v => $"N:{v}"); + return pr.PrintToString(p); + }, + shouldContain: ["N:Bob"] + )).SetName("ShouldBeTrue_When_SerializeForProperty_AppliesCustomSerializer"); + + yield return new TestCaseData(new TestSpec( + "Trim string property Name to length 3", + () => + { + var p = new Person { Name = "abcdef" }; + var pr = ClassPrinter.For().Trim(p => p.Name, 3); + return pr.PrintToString(p); + }, + shouldContain: ["abc"] + )).SetName("ShouldBeTrue_When_TrimString_Trims"); + + yield return new TestCaseData(new TestSpec( + "Trim multiple string properties independently", + () => + { + var p = new Person { Name = "abcdef", Note = "uvwxyz" }; + var pr = ClassPrinter.For() + .Trim(p => p.Name, 3) + .Trim(p => p.Note, 4); + return pr.PrintToString(p); + }, + shouldContain: ["abc", "uvwx"] + )).SetName("ShouldBeTrue_When_TrimMultipleStrings_EachTrimmedIndependently"); + + yield return new TestCaseData(new TestSpec( + "Set numeric culture to InvariantCulture for double Height", + () => + { + var p = new Person { Height = 12.5 }; + var pr = ClassPrinter.For().SetNumericCulture(CultureInfo.InvariantCulture); + return pr.PrintToString(p); + }, + shouldContain: ["12.5"] + )).SetName("ShouldBeTrue_When_NumericCulture_UsesInvariantCulture"); + + yield return new TestCaseData(new TestSpec( + "Both custom serialization and trimming apply to Name", + () => + { + var p = new Person { Name = "Benjamin" }; + var pr = ClassPrinter.For() + .Serialize(x => x.Name, s => $"[{s}]") + .Trim(x => x.Name, 4); + return pr.PrintToString(p); + }, + shouldContain: ["[Ben"] + )).SetName("ShouldBeTrue_When_CustomSerializationAndTrimming_BothApply"); + + yield return new TestCaseData(new TestSpec( + "Type exclusion and property exclusion both should be applied", + () => + { + var p = new Person { Age = 20, Height = 170 }; + var pr = ClassPrinter.For() + .Exclude() + .Exclude(x => x.Height); + return pr.PrintToString(p); + }, + shouldNotContain: ["20", "170"] + )).SetName("ShouldBeTrue_When_TypeExcludeAndPropertyExclude_BothApply"); + + yield return new TestCaseData(new TestSpec( + "Cyclic references should not cause stack overflow and show marker", + () => + { + var a = new Person { Name = "A" }; + var b = new Person { Name = "B" }; + a.Child = b; + b.Parent = a; + var pr = ClassPrinter.For(); + return pr.PrintToString(a); + }, + shouldContain: [""] + )).SetName("ShouldBeTrue_When_CyclicReferences_DoNotStackOverflow"); + + yield return new TestCaseData(new TestSpec( + "Default extension PrintToString() should work without explicit printer", + () => + { + var p = new Person { Name = "Alpha" }; + return p.PrintToString(); + }, + shouldContain: ["Alpha"] + )).SetName("ShouldBeTrue_When_DefaultExtensionMethod_Works"); + + yield return new TestCaseData(new TestSpec( + "Extension method with config should apply trimming when passed", + () => + { + var p = new Person { Name = "Beta" }; + return p.PrintToString(c => c.Trim(x => x.Name, 2)); + }, + shouldContain: ["Be"] + )).SetName("ShouldBeTrue_When_ExtensionMethodWithConfig_Applies"); + + yield return new TestCaseData(new TestSpec( + "Array serialization of Person[] should include element names", + () => + { + var arr = new[] { new Person { Name = "A" }, new Person { Name = "B" } }; + return ClassPrinter.For().PrintToString(arr); + }, + shouldContain: ["A", "B"] + )).SetName("ShouldBeTrue_When_ArraySerialization_Works"); + + yield return new TestCaseData(new TestSpec( + "List serialization should include element data", + () => + { + var list = new List { new Person { Name = "X" } }; + return ClassPrinter.For>().PrintToString(list); + }, + shouldContain: ["X"] + )).SetName("ShouldBeTrue_When_ListSerialization_Works"); + + yield return new TestCaseData(new TestSpec( + "Dictionary serialization should include key and nested name", + () => + { + var d = new Dictionary { { 1, new Person { Name = "K" } } }; + return ClassPrinter.For>().PrintToString(d); + }, + shouldContain: ["1", "K"] + )).SetName("ShouldBeTrue_When_DictionarySerialization_Works"); + + yield return new TestCaseData(new TestSpec( + "Nullable with value should serialize the value", + () => + { + var p = new Person { LuckyNumber = 7 }; + return ClassPrinter.For().PrintToString(p); + }, + shouldContain: ["7"] + )).SetName("ShouldBeTrue_When_NullableType_SerializesValue"); + + yield return new TestCaseData(new TestSpec( + "Nullable null should not print the property", + () => + { + var p = new Person { LuckyNumber = null }; + return ClassPrinter.For().PrintToString(p); + }, + shouldNotContain: ["LuckyNumber"] + )).SetName("ShouldBeTrue_When_NullableTypeNull_PrintsNull"); + + yield return new TestCaseData(new TestSpec( + "Deep nested collections (matrix) should serialize inner ints", + () => + { + var p = new Person + { + Matrix = + [ + [1, 2], + [3, 4] + ] + }; + return ClassPrinter.For().PrintToString(p); + }, + shouldContain: ["1", "4"] + )).SetName("ShouldBeTrue_When_DeepNestedCollections_Serialize"); + + yield return new TestCaseData(new TestSpec( + "Custom serializer for int type should affect nested objects (Friends list)", + () => + { + var p = new Person { Friends = new List { new Person { Age = 10 } } }; + var pr = ClassPrinter.For().Serialize(x => $"[{x}]"); + return pr.PrintToString(p); + }, + shouldContain: ["[10]"] + )).SetName("ShouldBeTrue_When_CustomSerializerForType_AffectsNestedObjects"); + + yield return new TestCaseData(new TestSpec( + "Set numeric culture should affect decimal Salary representation", + () => + { + var p = new Person { Salary = 1234.567m }; + var pr = ClassPrinter.For().SetNumericCulture(CultureInfo.InvariantCulture); + return pr.PrintToString(p); + }, + shouldContain: ["1234.567"] + )).SetName("ShouldBeTrue_When_Culture_AffectsDecimal"); + + yield return new TestCaseData(new TestSpec( + "Trim on one property should not affect other string properties", + () => + { + var p = new Person { Name = "abcdef", Note = "xyz" }; + var pr = ClassPrinter.For().Trim(x => x.Name, 2); + return pr.PrintToString(p); + }, + shouldContain: ["ab", "xyz"] + )).SetName("ShouldBeTrue_When_TrimOnProperty_DoesNotAffectOtherStrings"); + + yield return new TestCaseData(new TestSpec( + "Exclude a property then serialize another should still serialize the non-excluded property", + () => + { + var p = new Person { Name = "Qwerty" }; + var pr = ClassPrinter.For() + .Exclude(x => x.Note) + .Serialize(x => x.Name, v => v.ToUpper()); + return pr.PrintToString(p); + }, + shouldContain: ["QWERTY"] + )).SetName("ShouldBeTrue_When_ExcludeThenSerialize_SerializesNotExcluded"); + + yield return new TestCaseData(new TestSpec( + "Serialize type and exclude same property: property exclusion should win (no serialized value)", + () => + { + var p = new Person { Name = "Bob", Age = 20 }; + var pr = ClassPrinter.For() + .Serialize(x => $"I{x}") + .Exclude(x => x.Age); + return pr.PrintToString(p); + }, + shouldNotContain: ["I20"] + )).SetName("ShouldBeTrue_When_SerializeAndExcludeSameType_PropertyExclusionWins"); + + yield return new TestCaseData(new TestSpec( + "Property-specific serializer vs type serializer: original asserts I30 (kept as in original)", + () => + { + var p = new Person { Age = 30 }; + var pr = ClassPrinter.For() + .Serialize(x => $"I{x}") + .Serialize(x => x.Age, v => $"A{v}"); + return pr.PrintToString(p); + }, + shouldContain: ["I30"] + )).SetName("ShouldBeTrue_When_PropertySpecificSerializer_OverrideCheck"); + + yield return new TestCaseData(new TestSpec( + "Very deep object graph should not overflow (chain length 15)", + () => + { + var root = new Person { Name = "Root" }; + var cur = root; + for (var i = 0; i < 15; i++) + { + cur.Child = new Person { Name = "L" + i }; + cur = cur.Child; + } + + var pr = ClassPrinter.For(); + return pr.PrintToString(root); + }, + shouldContain: ["L14"] + )).SetName("ShouldBeTrue_When_VeryDeepObjectGraph_DoesNotOverflow"); + + yield return new TestCaseData(new TestSpec( + "Empty list should be serialized gracefully and property name present", + () => + { + var p = new Person { Friends = new List() }; + return ClassPrinter.For().PrintToString(p); + }, + shouldContain: ["Friends"] + )).SetName("ShouldBeTrue_When_EmptyList_SerializesGracefully"); + + yield return new TestCaseData(new TestSpec( + "Null property (Parent) should not be printed", + () => + { + var p = new Person { Parent = null }; + return ClassPrinter.For().PrintToString(p); + }, + shouldNotContain: ["Parent"] + )).SetName("ShouldBeTrue_When_NullProperty_NotPrinted"); + + yield return new TestCaseData(new TestSpec( + "Multiple custom serializers for int and double should both apply", + () => + { + var p = new Person { Age = 10, Height = 170 }; + var pr = ClassPrinter.For() + .Serialize(x => $"I{x}") + .Serialize(x => $"D{x}"); + return pr.PrintToString(p); + }, + shouldContain: ["I10", "D170"] + )).SetName("ShouldBeTrue_When_MultipleCustomSerializers_ApplyAll"); + + yield return new TestCaseData(new TestSpec( + "Serialize type in dictionary values", + () => + { + var d = new Dictionary { { "a", 5 }, { "b", 3 }, { "c", 4 } }; + var pr = ClassPrinter.For>() + .Serialize(i => $"[{i}]"); + return pr.PrintToString(d); + }, + shouldContain: ["[5]", "[3]"] + )).SetName("ShouldBeTrue_When_SerializeTypeInDictionary_Works"); + + yield return new TestCaseData(new TestSpec( + "Serialize int[] with custom int serializer", + () => + { + var arr = execute; + var pr = ClassPrinter.For() + .Serialize(x => $"#{x}"); + return pr.PrintToString(arr); + }, + shouldContain: ["#1", "#2", "#3"] + )).SetName("ShouldBeTrue_When_SerializeTypeInArray_Works"); + } } } } \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Tests/Person.cs b/ObjectPrinting/HomeWork/Tests/Person.cs index d18e23f23..2c76adb0e 100644 --- a/ObjectPrinting/HomeWork/Tests/Person.cs +++ b/ObjectPrinting/HomeWork/Tests/Person.cs @@ -2,11 +2,15 @@ { 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 Parent { get; set; } public Person Child { get; set; } + public double Height { get; set; } + public string Note { get; set; } + public decimal Salary { get; set; } + public List Friends { get; set; } + public int? LuckyNumber { get; set; } + public List> Matrix { get; set; } } } \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Tests/TestSpec.cs b/ObjectPrinting/HomeWork/Tests/TestSpec.cs new file mode 100644 index 000000000..f9d4864dc --- /dev/null +++ b/ObjectPrinting/HomeWork/Tests/TestSpec.cs @@ -0,0 +1,15 @@ +namespace ObjectPrinting.HomeWork.Tests; + +public class TestSpec( + string description, + Func execute, + string[]? shouldContain = null, + string[]? shouldNotContain = null) +{ + public string Description { get; } = description; + public Func Execute { get; } = execute; + public string[] ShouldContain { get; } = shouldContain ?? []; + public string[] ShouldNotContain { get; } = shouldNotContain ?? []; + + public override string ToString() => Description; +} \ No newline at end of file From 594b61a9f5f6460ac9e90939e3f5082dac5e9e1e Mon Sep 17 00:00:00 2001 From: younggogy Date: Thu, 20 Nov 2025 00:19:43 +0500 Subject: [PATCH 07/14] =?UTF-8?q?=D0=92=D1=8B=D0=BD=D0=B5=D1=81=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=BF=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D1=8B=20=D1=80?= =?UTF-8?q?=D0=B0=D1=81=D1=88=D0=B8=D1=80=D0=B5=D0=BD=D0=B8=D1=8F,=20?= =?UTF-8?q?=D1=87=D1=82=D0=BE=D0=B1=D1=8B=20=D1=83=D0=B4=D0=BE=D0=B1=D0=BD?= =?UTF-8?q?=D0=BE=20=D0=B1=D1=8B=D0=BB=D0=BE=20=D0=B2=D1=8B=D0=B7=D1=8B?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D1=8C=20PrintToString?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/ObjectPrinterExtensions.cs | 9 +++ .../Extensions/PrintingConfigExtensions.cs | 11 ++++ ObjectPrinting/HomeWork/ObjectPrinter.cs | 12 +++- .../Tests/ObjectPrinterAcceptanceTests.cs | 56 +++++++++---------- 4 files changed, 57 insertions(+), 31 deletions(-) create mode 100644 ObjectPrinting/HomeWork/Extensions/ObjectPrinterExtensions.cs create mode 100644 ObjectPrinting/HomeWork/Extensions/PrintingConfigExtensions.cs diff --git a/ObjectPrinting/HomeWork/Extensions/ObjectPrinterExtensions.cs b/ObjectPrinting/HomeWork/Extensions/ObjectPrinterExtensions.cs new file mode 100644 index 000000000..d55655c0a --- /dev/null +++ b/ObjectPrinting/HomeWork/Extensions/ObjectPrinterExtensions.cs @@ -0,0 +1,9 @@ +namespace ObjectPrinting.HomeWork.Extensions; + +public static class ObjectPrinterExtensions +{ + public static string PrintToString(this T obj) + { + return ObjectPrinter.For().PrintToString(obj); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Extensions/PrintingConfigExtensions.cs b/ObjectPrinting/HomeWork/Extensions/PrintingConfigExtensions.cs new file mode 100644 index 000000000..dd390cb9c --- /dev/null +++ b/ObjectPrinting/HomeWork/Extensions/PrintingConfigExtensions.cs @@ -0,0 +1,11 @@ +using ObjectPrinting.HomeWork.PrintUtils; + +namespace ObjectPrinting.HomeWork.Extensions; + +public static class PrintingConfigExtensions +{ + public static string PrintToString(this T obj, Func, PrintingConfig> config) + { + return config(ObjectPrinter.For()).PrintToString(obj); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/ObjectPrinter.cs b/ObjectPrinting/HomeWork/ObjectPrinter.cs index 8ef39476c..9d82a87c2 100644 --- a/ObjectPrinting/HomeWork/ObjectPrinter.cs +++ b/ObjectPrinting/HomeWork/ObjectPrinter.cs @@ -1,6 +1,7 @@ using ObjectPrinting.HomeWork.PrintUtils; using ObjectPrinting.HomeWork.PrintUtils.Implementations; -using ObjectPrinting.HomeWork.PrintUtils.Interfaces; +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; using ObjectPrinting.HomeWork.RuleUtils.Implementations; namespace ObjectPrinting.HomeWork; @@ -10,7 +11,12 @@ public class ObjectPrinter public static PrintingConfig For() { var ruleProcessor = new RuleProcessor(); - var cycleForammter = new CycleFormatter(ruleProcessor); - return new PrintingConfig(ruleProcessor, new PrintingProcessor(ruleProcessor, cycleForammter)); + var renderProperty = new PropertyRenderer(); + var strategies = new List(); + strategies.Add(new EnumerablePrinterStrategy()); + strategies.Add(new SimplePrinter(ruleProcessor)); + strategies.Add(new ObjectPrinterStrategy(renderProperty, ruleProcessor)); + strategies.Add(new CycleFormatterStrategy()); + return new PrintingConfig(ruleProcessor, new PrintingProcessor(strategies)); } } \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs index 3137a49c8..53859b5d7 100644 --- a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs @@ -39,7 +39,7 @@ public static IEnumerable TestCases () => { var p = new Person { Name = "A", Age = 10 }; - var pr = ClassPrinter.For().Exclude(); + var pr = ObjectPrinter.For().Exclude(); return pr.PrintToString(p); }, shouldNotContain: ["10"] @@ -50,7 +50,7 @@ public static IEnumerable TestCases () => { var p = new Person { Height = 180 }; - var pr = ClassPrinter.For().Exclude(x => x.Height); + var pr = ObjectPrinter.For().Exclude(x => x.Height); return pr.PrintToString(p); }, shouldNotContain: ["180"] @@ -61,7 +61,7 @@ public static IEnumerable TestCases () => { var p = new Person { Age = 5 }; - var pr = ClassPrinter.For().Serialize(x => $"[{x}]"); + var pr = ObjectPrinter.For().Serialize(x => $"[{x}]"); return pr.PrintToString(p); }, shouldContain: ["[5]"] @@ -72,7 +72,7 @@ public static IEnumerable TestCases () => { var p = new Person { Name = "Bob" }; - var pr = ClassPrinter.For().Serialize(x => x.Name, v => $"N:{v}"); + var pr = ObjectPrinter.For().Serialize(x => x.Name, v => $"N:{v}"); return pr.PrintToString(p); }, shouldContain: ["N:Bob"] @@ -83,7 +83,7 @@ public static IEnumerable TestCases () => { var p = new Person { Name = "abcdef" }; - var pr = ClassPrinter.For().Trim(p => p.Name, 3); + var pr = ObjectPrinter.For().Trim(p => p.Name, 3); return pr.PrintToString(p); }, shouldContain: ["abc"] @@ -94,7 +94,7 @@ public static IEnumerable TestCases () => { var p = new Person { Name = "abcdef", Note = "uvwxyz" }; - var pr = ClassPrinter.For() + var pr = ObjectPrinter.For() .Trim(p => p.Name, 3) .Trim(p => p.Note, 4); return pr.PrintToString(p); @@ -107,7 +107,7 @@ public static IEnumerable TestCases () => { var p = new Person { Height = 12.5 }; - var pr = ClassPrinter.For().SetNumericCulture(CultureInfo.InvariantCulture); + var pr = ObjectPrinter.For().SetNumericCulture(CultureInfo.InvariantCulture); return pr.PrintToString(p); }, shouldContain: ["12.5"] @@ -118,7 +118,7 @@ public static IEnumerable TestCases () => { var p = new Person { Name = "Benjamin" }; - var pr = ClassPrinter.For() + var pr = ObjectPrinter.For() .Serialize(x => x.Name, s => $"[{s}]") .Trim(x => x.Name, 4); return pr.PrintToString(p); @@ -131,7 +131,7 @@ public static IEnumerable TestCases () => { var p = new Person { Age = 20, Height = 170 }; - var pr = ClassPrinter.For() + var pr = ObjectPrinter.For() .Exclude() .Exclude(x => x.Height); return pr.PrintToString(p); @@ -147,7 +147,7 @@ public static IEnumerable TestCases var b = new Person { Name = "B" }; a.Child = b; b.Parent = a; - var pr = ClassPrinter.For(); + var pr = ObjectPrinter.For(); return pr.PrintToString(a); }, shouldContain: [""] @@ -178,7 +178,7 @@ public static IEnumerable TestCases () => { var arr = new[] { new Person { Name = "A" }, new Person { Name = "B" } }; - return ClassPrinter.For().PrintToString(arr); + return ObjectPrinter.For().PrintToString(arr); }, shouldContain: ["A", "B"] )).SetName("ShouldBeTrue_When_ArraySerialization_Works"); @@ -188,7 +188,7 @@ public static IEnumerable TestCases () => { var list = new List { new Person { Name = "X" } }; - return ClassPrinter.For>().PrintToString(list); + return ObjectPrinter.For>().PrintToString(list); }, shouldContain: ["X"] )).SetName("ShouldBeTrue_When_ListSerialization_Works"); @@ -198,7 +198,7 @@ public static IEnumerable TestCases () => { var d = new Dictionary { { 1, new Person { Name = "K" } } }; - return ClassPrinter.For>().PrintToString(d); + return ObjectPrinter.For>().PrintToString(d); }, shouldContain: ["1", "K"] )).SetName("ShouldBeTrue_When_DictionarySerialization_Works"); @@ -208,7 +208,7 @@ public static IEnumerable TestCases () => { var p = new Person { LuckyNumber = 7 }; - return ClassPrinter.For().PrintToString(p); + return ObjectPrinter.For().PrintToString(p); }, shouldContain: ["7"] )).SetName("ShouldBeTrue_When_NullableType_SerializesValue"); @@ -218,7 +218,7 @@ public static IEnumerable TestCases () => { var p = new Person { LuckyNumber = null }; - return ClassPrinter.For().PrintToString(p); + return ObjectPrinter.For().PrintToString(p); }, shouldNotContain: ["LuckyNumber"] )).SetName("ShouldBeTrue_When_NullableTypeNull_PrintsNull"); @@ -235,7 +235,7 @@ public static IEnumerable TestCases [3, 4] ] }; - return ClassPrinter.For().PrintToString(p); + return ObjectPrinter.For().PrintToString(p); }, shouldContain: ["1", "4"] )).SetName("ShouldBeTrue_When_DeepNestedCollections_Serialize"); @@ -245,7 +245,7 @@ public static IEnumerable TestCases () => { var p = new Person { Friends = new List { new Person { Age = 10 } } }; - var pr = ClassPrinter.For().Serialize(x => $"[{x}]"); + var pr = ObjectPrinter.For().Serialize(x => $"[{x}]"); return pr.PrintToString(p); }, shouldContain: ["[10]"] @@ -256,7 +256,7 @@ public static IEnumerable TestCases () => { var p = new Person { Salary = 1234.567m }; - var pr = ClassPrinter.For().SetNumericCulture(CultureInfo.InvariantCulture); + var pr = ObjectPrinter.For().SetNumericCulture(CultureInfo.InvariantCulture); return pr.PrintToString(p); }, shouldContain: ["1234.567"] @@ -267,7 +267,7 @@ public static IEnumerable TestCases () => { var p = new Person { Name = "abcdef", Note = "xyz" }; - var pr = ClassPrinter.For().Trim(x => x.Name, 2); + var pr = ObjectPrinter.For().Trim(x => x.Name, 2); return pr.PrintToString(p); }, shouldContain: ["ab", "xyz"] @@ -278,7 +278,7 @@ public static IEnumerable TestCases () => { var p = new Person { Name = "Qwerty" }; - var pr = ClassPrinter.For() + var pr = ObjectPrinter.For() .Exclude(x => x.Note) .Serialize(x => x.Name, v => v.ToUpper()); return pr.PrintToString(p); @@ -291,7 +291,7 @@ public static IEnumerable TestCases () => { var p = new Person { Name = "Bob", Age = 20 }; - var pr = ClassPrinter.For() + var pr = ObjectPrinter.For() .Serialize(x => $"I{x}") .Exclude(x => x.Age); return pr.PrintToString(p); @@ -304,7 +304,7 @@ public static IEnumerable TestCases () => { var p = new Person { Age = 30 }; - var pr = ClassPrinter.For() + var pr = ObjectPrinter.For() .Serialize(x => $"I{x}") .Serialize(x => x.Age, v => $"A{v}"); return pr.PrintToString(p); @@ -324,7 +324,7 @@ public static IEnumerable TestCases cur = cur.Child; } - var pr = ClassPrinter.For(); + var pr = ObjectPrinter.For(); return pr.PrintToString(root); }, shouldContain: ["L14"] @@ -335,7 +335,7 @@ public static IEnumerable TestCases () => { var p = new Person { Friends = new List() }; - return ClassPrinter.For().PrintToString(p); + return ObjectPrinter.For().PrintToString(p); }, shouldContain: ["Friends"] )).SetName("ShouldBeTrue_When_EmptyList_SerializesGracefully"); @@ -345,7 +345,7 @@ public static IEnumerable TestCases () => { var p = new Person { Parent = null }; - return ClassPrinter.For().PrintToString(p); + return ObjectPrinter.For().PrintToString(p); }, shouldNotContain: ["Parent"] )).SetName("ShouldBeTrue_When_NullProperty_NotPrinted"); @@ -355,7 +355,7 @@ public static IEnumerable TestCases () => { var p = new Person { Age = 10, Height = 170 }; - var pr = ClassPrinter.For() + var pr = ObjectPrinter.For() .Serialize(x => $"I{x}") .Serialize(x => $"D{x}"); return pr.PrintToString(p); @@ -368,7 +368,7 @@ public static IEnumerable TestCases () => { var d = new Dictionary { { "a", 5 }, { "b", 3 }, { "c", 4 } }; - var pr = ClassPrinter.For>() + var pr = ObjectPrinter.For>() .Serialize(i => $"[{i}]"); return pr.PrintToString(d); }, @@ -380,7 +380,7 @@ public static IEnumerable TestCases () => { var arr = execute; - var pr = ClassPrinter.For() + var pr = ObjectPrinter.For() .Serialize(x => $"#{x}"); return pr.PrintToString(arr); }, From b00447762ecce47839513b7901d4af365f272105 Mon Sep 17 00:00:00 2001 From: younggogy Date: Thu, 20 Nov 2025 00:21:06 +0500 Subject: [PATCH 08/14] =?UTF-8?q?=D0=9F=D0=BE=D1=87=D0=B8=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BA=D0=BE=D0=B4=20=D0=B2=20=D0=BA=D0=BB=D0=B0?= =?UTF-8?q?=D1=81=D1=81=D0=B5=20PrintingConfig.cs.=20=D0=92=D1=8B=D0=BD?= =?UTF-8?q?=D0=B5=D1=81=20=D0=B2=20=D0=BF=D0=B0=D1=82=D1=82=D0=B5=D1=80?= =?UTF-8?q?=D0=BD=20Strategy=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20?= =?UTF-8?q?=D1=81=20=D0=BF=D1=80=D0=B8=D0=BD=D1=82=D0=B0=D0=BC=D0=B8=20?= =?UTF-8?q?=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D1=85=20=D0=BE?= =?UTF-8?q?=D0=B1=D1=8C=D0=B5=D0=BA=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/HomeWork/ObjectPrinter.cs | 2 +- .../Implementations/CycleFormatter.cs | 27 -------- .../Implementations/PrintingProcessor.cs | 69 ++++--------------- .../Implementations/PropertyRenderer.cs | 37 ++++++++++ .../PrintUtils/Interfaces/ICycleFormatter.cs | 7 -- .../Interfaces/IPropertyRenderer.cs | 15 ++++ .../Implementations/CycleFormatterStrategy.cs | 28 ++++++++ .../EnumerablePrinterStrategy.cs | 39 +++++++++++ .../Implementations/ObjectPrinterStrategy.cs | 51 ++++++++++++++ .../Implementations/SimplePrinterStrategy.cs | 25 +++++++ .../Strategies/Interfaces/IPrintStrategy.cs | 7 ++ 11 files changed, 218 insertions(+), 89 deletions(-) delete mode 100644 ObjectPrinting/HomeWork/PrintUtils/Implementations/CycleFormatter.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Implementations/PropertyRenderer.cs delete mode 100644 ObjectPrinting/HomeWork/PrintUtils/Interfaces/ICycleFormatter.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Interfaces/IPropertyRenderer.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/EnumerablePrinterStrategy.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Strategies/Interfaces/IPrintStrategy.cs diff --git a/ObjectPrinting/HomeWork/ObjectPrinter.cs b/ObjectPrinting/HomeWork/ObjectPrinter.cs index 9d82a87c2..49b72bed3 100644 --- a/ObjectPrinting/HomeWork/ObjectPrinter.cs +++ b/ObjectPrinting/HomeWork/ObjectPrinter.cs @@ -14,7 +14,7 @@ public static PrintingConfig For() var renderProperty = new PropertyRenderer(); var strategies = new List(); strategies.Add(new EnumerablePrinterStrategy()); - strategies.Add(new SimplePrinter(ruleProcessor)); + strategies.Add(new SimplePrinterStrategy(ruleProcessor)); strategies.Add(new ObjectPrinterStrategy(renderProperty, ruleProcessor)); strategies.Add(new CycleFormatterStrategy()); return new PrintingConfig(ruleProcessor, new PrintingProcessor(strategies)); diff --git a/ObjectPrinting/HomeWork/PrintUtils/Implementations/CycleFormatter.cs b/ObjectPrinting/HomeWork/PrintUtils/Implementations/CycleFormatter.cs deleted file mode 100644 index a1661c1c4..000000000 --- a/ObjectPrinting/HomeWork/PrintUtils/Implementations/CycleFormatter.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using ObjectPrinting.HomeWork.PrintUtils.Interfaces; -using ObjectPrinting.HomeWork.RuleUtils.Interfaces; - -namespace ObjectPrinting.HomeWork.PrintUtils.Implementations; - -public class CycleFormatter(IRuleProcessor ruleProcessor) : ICycleFormatter -{ - private readonly HashSet visited = []; - public string FormatReference(object obj) - { - var type = obj.GetType(); - //TODO доделать реализацию, сейчас есть ошибка в логике. Если поле хотят exclude тогда, неясно будет, в цикле, что нам указывать - var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); - var basicProperty = properties.Length > 1 ? properties[1] : - properties.Length > 0 ? properties[0] : null; - var basicValue = basicProperty != null - ? ruleProcessor.ApplyRule(basicProperty.GetValue(obj), basicProperty).Value - : "?"; - return $""; - } - - public bool TryMark(object obj) - { - return visited.Add(obj); - } -} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs index 57147993d..4e23a63ff 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs @@ -1,70 +1,31 @@ -using System.Reflection; -using System.Text; using ObjectPrinting.HomeWork.PrintUtils.Interfaces; -using ObjectPrinting.HomeWork.RuleUtils.Dto; -using ObjectPrinting.HomeWork.RuleUtils.Interfaces; +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; namespace ObjectPrinting.HomeWork.PrintUtils.Implementations; -public class PrintingProcessor(IRuleProcessor ruleProcessor, ICycleFormatter cycleFormatter) : IPrintingProcessor +public class PrintingProcessor( + IEnumerable strategies) + : IPrintingProcessor { + private readonly List strategies = strategies.ToList(); + public string Print(object? obj, int nestingLevel, HashSet visited) { - var (text, type) = HandleNullOrVisited(obj); - if (type is null) - { - return text; - } + if (obj == null) return "null"; - var sb = new StringBuilder(); - sb.AppendLine(type.Name); + var type = obj.GetType(); - var indent = new string('\t', nestingLevel + 1); - - foreach (var prop in type.GetProperties()) + foreach (var strategy in strategies.OrderByDescending(strategy => strategy is CycleFormatterStrategy)) { - var value = prop.GetValue(obj); - if (value == null) continue; + if (!strategy.CanHandle(type)) continue; - AppendProperty(sb, prop, value, nestingLevel, visited, indent); + var result = strategy.Print(obj, nestingLevel, visited, Print); + if (result != null) + return result; } - return sb.ToString().TrimEnd(); - } - - private (string? text, Type? type) HandleNullOrVisited(object? obj) - { - if (obj == null) - return ("null", null); - - var firstSeen = cycleFormatter.TryMark(obj); - if (!firstSeen) - return (cycleFormatter.FormatReference(obj), null); - - return (null, obj.GetType()); + throw new InvalidOperationException($"No strategy could print object of type {type.Name}"); } - private void AppendProperty(StringBuilder sb, PropertyInfo prop, object value, int nestingLevel, - HashSet visited, string indent) - { - var typeValue = value.GetType(); - var defaultValue = typeValue.IsValueType ? Activator.CreateInstance(typeValue) : null; - if (value.Equals(defaultValue)) return; - - var ruleOutcome = ruleProcessor.ApplyRule(value, prop); - if (ruleOutcome.Action == RuleResult.Skip) return; - - sb.Append(indent + prop.Name + " = "); - - if (!(typeValue.IsPrimitive || typeValue == typeof(string)) && ruleOutcome.Value == value.ToString()) - { - sb.Append(Print(value, nestingLevel + 1, visited)); - } - else - { - sb.Append(ruleOutcome.Value); - } - - sb.Append('\n'); - } } \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Implementations/PropertyRenderer.cs b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PropertyRenderer.cs new file mode 100644 index 000000000..ad76d2d81 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PropertyRenderer.cs @@ -0,0 +1,37 @@ +using System.Collections; +using System.Reflection; +using ObjectPrinting.HomeWork.PrintUtils.Helpers; +using ObjectPrinting.HomeWork.PrintUtils.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; + +namespace ObjectPrinting.HomeWork.PrintUtils.Implementations; + +public class PropertyRenderer : IPropertyRenderer +{ + public string? RenderProperty( + object target, + PropertyInfo prop, + int nestingLevel, + HashSet visited, + IRuleProcessor ruleProcessor, + Func, string> recursivePrinter) + { + var value = prop.GetValue(target); + if (value == null) return null; + + var ruleOutcome = ruleProcessor.ApplyRule(value, prop); + if (ruleOutcome.Action == RuleResult.Skip) return null; + + var type = value.GetType(); + + if (typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string) || !SimpleHelper.IsSimple(type)) + { + var printed = recursivePrinter(value, nestingLevel + 1, visited); + return $"{prop.Name} =\n{printed}"; + } + + var formatted = ruleOutcome.Value ?? value.ToString(); + return $"{prop.Name} = {formatted}"; + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Interfaces/ICycleFormatter.cs b/ObjectPrinting/HomeWork/PrintUtils/Interfaces/ICycleFormatter.cs deleted file mode 100644 index 80a5fb174..000000000 --- a/ObjectPrinting/HomeWork/PrintUtils/Interfaces/ICycleFormatter.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ObjectPrinting.HomeWork.PrintUtils.Interfaces; - -public interface ICycleFormatter -{ - string FormatReference(object obj); - public bool TryMark(object obj); -} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Interfaces/IPropertyRenderer.cs b/ObjectPrinting/HomeWork/PrintUtils/Interfaces/IPropertyRenderer.cs new file mode 100644 index 000000000..5694ff422 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Interfaces/IPropertyRenderer.cs @@ -0,0 +1,15 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; + +namespace ObjectPrinting.HomeWork.PrintUtils.Interfaces; + +public interface IPropertyRenderer +{ + public string? RenderProperty( + object target, + PropertyInfo prop, + int nestingLevel, + HashSet visited, + IRuleProcessor ruleProcessor, + Func, string> recursivePrinter); +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs new file mode 100644 index 000000000..ae4ede163 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs @@ -0,0 +1,28 @@ +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; + +public class CycleFormatterStrategy : IPrintStrategy +{ + private readonly Dictionary visited = new(); + public bool CanHandle(Type type) => true; + + public string Print(object obj, int nestingLevel, HashSet ignoredVisited, + Func, string> ignoredRecursivePrinter) + { + if (visited.TryGetValue(obj, out var originalLevel)) + { + return FormatReference(obj, originalLevel); + } + + visited[obj] = nestingLevel; + + return null; + } + + private string FormatReference(object obj, int nestingLevel) + { + var type = obj.GetType(); + return $""; + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/EnumerablePrinterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/EnumerablePrinterStrategy.cs new file mode 100644 index 000000000..e29ceb318 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/EnumerablePrinterStrategy.cs @@ -0,0 +1,39 @@ +using System.Collections; +using System.Text; +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; + +public class EnumerablePrinterStrategy : IPrintStrategy +{ + public bool CanHandle(Type type) => typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string); + + public string Print(object obj, int nestingLevel, HashSet visited, + Func, string> recursivePrinter) + { + return PrintEnumerable((IEnumerable)obj, nestingLevel, visited, recursivePrinter); + } + + private string PrintEnumerable(IEnumerable enumerable, int nestingLevel, HashSet visited, + Func, string> recursivePrinter) + { + var indent = new string('\t', nestingLevel); + var sb = new StringBuilder(); + + sb.AppendLine(indent + enumerable.GetType().Name + " ["); + + foreach (var item in enumerable) + { + var itemText = recursivePrinter(item, nestingLevel, visited); + + var itemLines = itemText.Split(Environment.NewLine); + foreach (var line in itemLines) + { + sb.AppendLine(new string('\t', nestingLevel + 1) + line); + } + } + + sb.AppendLine(indent + "]"); + return sb.ToString(); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs new file mode 100644 index 000000000..ec21c92d3 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs @@ -0,0 +1,51 @@ +using System.Collections; +using System.Reflection; +using System.Text; +using ObjectPrinting.HomeWork.PrintUtils.Helpers; +using ObjectPrinting.HomeWork.PrintUtils.Interfaces; +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; + +namespace ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; + +public class ObjectPrinterStrategy( + IPropertyRenderer propertyRenderer, + IRuleProcessor ruleProcessor) + : IPrintStrategy +{ + public bool CanHandle(Type type) => !typeof(IEnumerable).IsAssignableFrom(type) && !SimpleHelper.IsSimple(type); + + public string Print(object obj, int nestingLevel, HashSet visited, + Func, string> recursivePrinter) + { + return PrintObject(obj, nestingLevel, visited, recursivePrinter, + ruleProcessor, propertyRenderer); + } + + private string PrintObject( + object obj, + int nestingLevel, + HashSet visited, + Func, string> recursivePrinter, + IRuleProcessor ruleProcessor, + IPropertyRenderer propertyRenderer) + { + var type = obj.GetType(); + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine(type.Name); + var indent = new string('\t', nestingLevel + 1); + + foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + var propLine = propertyRenderer.RenderProperty( + obj, prop, nestingLevel, visited, + ruleProcessor, recursivePrinter + ); + + if (!string.IsNullOrEmpty(propLine)) + stringBuilder.AppendLine(indent + propLine); + } + + return stringBuilder.ToString().TrimEnd(); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs new file mode 100644 index 000000000..4da1b2fd3 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs @@ -0,0 +1,25 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.PrintUtils.Helpers; +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; + +namespace ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; + +public class SimplePrinterStrategy(IRuleProcessor ruleProcessor) : IPrintStrategy +{ + public bool CanHandle(Type type) => SimpleHelper.IsSimple(type); + + public string Print(object obj, int nestingLevel, HashSet visited, + Func, string> recursivePrinter) + { + return Format(obj); + } + + private string Format(object obj, PropertyInfo? propInfo = null) + { + var outcome = ruleProcessor.ApplyRule(obj, propInfo); + if (outcome.Action == RuleResult.Skip) return "null"; + return outcome.Value ?? obj.ToString() ?? string.Empty; + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Interfaces/IPrintStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Interfaces/IPrintStrategy.cs new file mode 100644 index 000000000..469e94de3 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Interfaces/IPrintStrategy.cs @@ -0,0 +1,7 @@ +namespace ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; + +public interface IPrintStrategy +{ + bool CanHandle(Type type); + string Print(object obj, int nestingLevel, HashSet visited, Func, string> recursivePrinter); +} \ No newline at end of file From cff7d5995299f31f04e40cc107016cda2377012a Mon Sep 17 00:00:00 2001 From: younggogy Date: Thu, 20 Nov 2025 00:21:22 +0500 Subject: [PATCH 09/14] =?UTF-8?q?=D0=92=D1=8B=D0=BD=D0=B5=D1=81=20=D1=85?= =?UTF-8?q?=D0=B5=D0=BB=D0=BF=D0=B5=D1=80=20=D0=B4=D0=BB=D1=8F=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B8=20IsSimple?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HomeWork/PrintUtils/Helpers/SimpleHelper.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleHelper.cs diff --git a/ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleHelper.cs b/ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleHelper.cs new file mode 100644 index 000000000..2081cf7e3 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleHelper.cs @@ -0,0 +1,15 @@ +namespace ObjectPrinting.HomeWork.PrintUtils.Helpers; + +public static class SimpleHelper +{ + public static bool IsSimple(Type type) + { + return type.IsPrimitive + || type == typeof(string) + || type == typeof(decimal) + || type == typeof(DateTime) + || type == typeof(Guid) + || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && + IsSimple(type.GetGenericArguments()[0])); + } +} \ No newline at end of file From b22a07e89d8a2af27dbf2b3650b92054fa5ec084 Mon Sep 17 00:00:00 2001 From: younggogy Date: Thu, 20 Nov 2025 00:42:55 +0500 Subject: [PATCH 10/14] =?UTF-8?q?=D0=BD=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=BE=D0=B9=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3=20=D0=B2=20ObjectPrinter.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectPrinting/HomeWork/ObjectPrinter.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ObjectPrinting/HomeWork/ObjectPrinter.cs b/ObjectPrinting/HomeWork/ObjectPrinter.cs index 49b72bed3..b04e94864 100644 --- a/ObjectPrinting/HomeWork/ObjectPrinter.cs +++ b/ObjectPrinting/HomeWork/ObjectPrinter.cs @@ -12,11 +12,13 @@ public static PrintingConfig For() { var ruleProcessor = new RuleProcessor(); var renderProperty = new PropertyRenderer(); - var strategies = new List(); - strategies.Add(new EnumerablePrinterStrategy()); - strategies.Add(new SimplePrinterStrategy(ruleProcessor)); - strategies.Add(new ObjectPrinterStrategy(renderProperty, ruleProcessor)); - strategies.Add(new CycleFormatterStrategy()); + var strategies = new List + { + new EnumerablePrinterStrategy(), + new SimplePrinterStrategy(ruleProcessor), + new ObjectPrinterStrategy(renderProperty, ruleProcessor), + new CycleFormatterStrategy() + }; return new PrintingConfig(ruleProcessor, new PrintingProcessor(strategies)); } } \ No newline at end of file From 42ffc9df5d835fc7fb5377b2c4f6110918ce80e6 Mon Sep 17 00:00:00 2001 From: younggogy Date: Thu, 20 Nov 2025 00:48:14 +0500 Subject: [PATCH 11/14] =?UTF-8?q?=D0=9D=D0=B5=D0=B7=D0=BD=D0=B0=D1=87?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D1=80=D0=B5?= =?UTF-8?q?=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PrintUtils/Implementations/PrintingProcessor.cs | 4 +++- ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs | 5 ++--- .../Implementations/ObjectPrinterStrategy.cs | 10 ++++------ .../Implementations/SimplePrinterStrategy.cs | 5 ++++- .../RuleUtils/Implementations/RuleProcessor.cs | 4 +++- .../HomeWork/RuleUtils/Interfaces/IRuleProcessor.cs | 2 +- .../Strategies/Implementations/CultureRule.cs | 4 ++-- .../Strategies/Implementations/ExcludeRule.cs | 4 ++-- .../Strategies/Implementations/SerializationRule.cs | 4 ++-- .../Strategies/Implementations/TrimStringRule.cs | 4 ++-- .../Strategies/Interfaces/ISerializationRule.cs | 2 +- .../HomeWork/Tests/ObjectPrinterAcceptanceTests.cs | 4 +--- 12 files changed, 27 insertions(+), 25 deletions(-) diff --git a/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs index 4e23a63ff..9ea4d56ae 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs @@ -21,8 +21,10 @@ public string Print(object? obj, int nestingLevel, HashSet visited) if (!strategy.CanHandle(type)) continue; var result = strategy.Print(obj, nestingLevel, visited, Print); - if (result != null) + if (result != null) + { return result; + } } throw new InvalidOperationException($"No strategy could print object of type {type.Name}"); diff --git a/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs index 4ea554999..36c1bfe4d 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs @@ -3,7 +3,7 @@ using System.Reflection; using ObjectPrinting.HomeWork.PrintUtils.Interfaces; using ObjectPrinting.HomeWork.RuleUtils.Interfaces; -using ObjectPrinting.HomeWork.Strategies.Implementations; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; namespace ObjectPrinting.HomeWork.PrintUtils; @@ -45,8 +45,7 @@ public PrintingConfig Serialize( public PrintingConfig Trim(Expression> property, int length) { var body = property.Body as MemberExpression; - var propertyInfo = body?.Member as PropertyInfo; - if (propertyInfo is null) + if (body?.Member is not PropertyInfo propertyInfo) { throw new NullReferenceException(); } diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs index ec21c92d3..f0295ae0d 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs @@ -8,18 +8,14 @@ namespace ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; -public class ObjectPrinterStrategy( - IPropertyRenderer propertyRenderer, - IRuleProcessor ruleProcessor) - : IPrintStrategy +public class ObjectPrinterStrategy(IPropertyRenderer propertyRenderer, IRuleProcessor ruleProcessor) : IPrintStrategy { public bool CanHandle(Type type) => !typeof(IEnumerable).IsAssignableFrom(type) && !SimpleHelper.IsSimple(type); public string Print(object obj, int nestingLevel, HashSet visited, Func, string> recursivePrinter) { - return PrintObject(obj, nestingLevel, visited, recursivePrinter, - ruleProcessor, propertyRenderer); + return PrintObject(obj, nestingLevel, visited, recursivePrinter, ruleProcessor, propertyRenderer); } private string PrintObject( @@ -43,7 +39,9 @@ private string PrintObject( ); if (!string.IsNullOrEmpty(propLine)) + { stringBuilder.AppendLine(indent + propLine); + } } return stringBuilder.ToString().TrimEnd(); diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs index 4da1b2fd3..70c9ec6bc 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs @@ -19,7 +19,10 @@ public string Print(object obj, int nestingLevel, HashSet visited, private string Format(object obj, PropertyInfo? propInfo = null) { var outcome = ruleProcessor.ApplyRule(obj, propInfo); - if (outcome.Action == RuleResult.Skip) return "null"; + if (outcome.Action == RuleResult.Skip) + { + return "null"; + } return outcome.Value ?? obj.ToString() ?? string.Empty; } } \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs b/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs index 25c270d52..f4ac717ef 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs @@ -1,7 +1,7 @@ using System.Reflection; using ObjectPrinting.HomeWork.RuleUtils.Dto; using ObjectPrinting.HomeWork.RuleUtils.Interfaces; -using ObjectPrinting.HomeWork.Strategies.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; namespace ObjectPrinting.HomeWork.RuleUtils.Implementations; @@ -17,7 +17,9 @@ public void AddRule(ISerializationRule rule) public RuleOutcome ApplyRule(object? propertyValue, PropertyInfo? propertyInfo) { if (propertyValue == null) + { return new RuleOutcome(RuleResult.Print, "null"); + } var current = propertyValue; string? resultString = null; diff --git a/ObjectPrinting/HomeWork/RuleUtils/Interfaces/IRuleProcessor.cs b/ObjectPrinting/HomeWork/RuleUtils/Interfaces/IRuleProcessor.cs index 7a2b3cde2..d600afcef 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Interfaces/IRuleProcessor.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Interfaces/IRuleProcessor.cs @@ -1,6 +1,6 @@ using System.Reflection; using ObjectPrinting.HomeWork.RuleUtils.Dto; -using ObjectPrinting.HomeWork.Strategies.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; namespace ObjectPrinting.HomeWork.RuleUtils.Interfaces; diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs index 2b02eed96..3dcc745ca 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs @@ -1,9 +1,9 @@ using System.Globalization; using System.Reflection; using ObjectPrinting.HomeWork.RuleUtils.Dto; -using ObjectPrinting.HomeWork.Strategies.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; -namespace ObjectPrinting.HomeWork.Strategies.Implementations; +namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; public class CultureRule(CultureInfo cultureInfo) : ISerializationRule { diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs index 340d0bd35..5daceaf5b 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs @@ -1,8 +1,8 @@ using System.Reflection; using ObjectPrinting.HomeWork.RuleUtils.Dto; -using ObjectPrinting.HomeWork.Strategies.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; -namespace ObjectPrinting.HomeWork.Strategies.Implementations; +namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; public class ExcludeRule : ISerializationRule { diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs index 346ce7f30..4e7cb0812 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs @@ -1,8 +1,8 @@ using System.Reflection; using ObjectPrinting.HomeWork.RuleUtils.Dto; -using ObjectPrinting.HomeWork.Strategies.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; -namespace ObjectPrinting.HomeWork.Strategies.Implementations; +namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; public class SerializationRule(Func serializer, PropertyInfo? property = null) : ISerializationRule { diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs index 79919347a..7a9183281 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs @@ -1,8 +1,8 @@ using System.Reflection; using ObjectPrinting.HomeWork.RuleUtils.Dto; -using ObjectPrinting.HomeWork.Strategies.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; -namespace ObjectPrinting.HomeWork.Strategies.Implementations; +namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; public class TrimStringRule(PropertyInfo property, int length) : ISerializationRule { diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs index e2e365e2e..28c3664f9 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs @@ -1,7 +1,7 @@ using System.Reflection; using ObjectPrinting.HomeWork.RuleUtils.Dto; -namespace ObjectPrinting.HomeWork.Strategies.Interfaces; +namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; public interface ISerializationRule { diff --git a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs index 53859b5d7..28d1a3721 100644 --- a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Globalization; +using System.Globalization; using FluentAssertions; using NUnit.Framework; using ObjectPrinting.HomeWork.Extensions; From 7a0b41ef312e88458a0ced2a890252ebdfb73619 Mon Sep 17 00:00:00 2001 From: younggogy Date: Sat, 22 Nov 2025 22:50:58 +0500 Subject: [PATCH 12/14] =?UTF-8?q?=D0=9F=D0=B0=D1=80=D1=83=20=D1=84=D0=B8?= =?UTF-8?q?=D0=BA=D1=81=D0=BE=D0=B2=20=D0=BF=D0=BE=20=D0=B7=D0=B0=D0=BC?= =?UTF-8?q?=D0=B5=D1=87=D0=B0=D0=BD=D0=B8=D1=8F=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HomeWork/PrintUtils/PrintingConfig.cs | 39 ++++++++++++------- .../Implementations/CycleFormatterStrategy.cs | 3 +- .../Tests/ObjectPrinterAcceptanceTests.cs | 4 +- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs index 36c1bfe4d..aa4aa6d84 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs @@ -16,12 +16,11 @@ public string PrintToString(TOwner obj) public PrintingConfig Exclude() { - var type = typeof(T); - ruleProcessor.AddRule(new ExcludeRule(type)); + ruleProcessor.AddRule(new ExcludeRule(typeof(T))); return this; } - public PrintingConfig SetNumericCulture(CultureInfo culture) + public PrintingConfig SetFormattingCulture(CultureInfo culture) { ruleProcessor.AddRule(new CultureRule(culture)); return this; @@ -34,31 +33,41 @@ public PrintingConfig Serialize(Func serializer) } public PrintingConfig Serialize( - Expression> property, Func serializer) + Expression> property, + Func serializer) { - var body = property.Body as MemberExpression; - var propertyInfo = body?.Member as PropertyInfo; + var propertyInfo = GetPropertyInfo(property); ruleProcessor.AddRule(new SerializationRule(serializer, propertyInfo)); return this; } public PrintingConfig Trim(Expression> property, int length) { - var body = property.Body as MemberExpression; - if (body?.Member is not PropertyInfo propertyInfo) - { - throw new NullReferenceException(); - } - + var propertyInfo = GetPropertyInfo(property); ruleProcessor.AddRule(new TrimStringRule(propertyInfo, length)); return this; } public PrintingConfig Exclude(Expression> property) { - var body = property.Body as MemberExpression; - var propertyInfo = body?.Member as PropertyInfo; + var propertyInfo = GetPropertyInfo(property); ruleProcessor.AddRule(new ExcludeRule(propertyInfo)); return this; } -} \ No newline at end of file + + private static PropertyInfo GetPropertyInfo( + Expression> propertyExpression) + { + if (propertyExpression.Body is not MemberExpression memberExpr) + throw new ArgumentException( + $"Expression '{propertyExpression}' must be a simple property access", + nameof(propertyExpression)); + + if (memberExpr.Member is not PropertyInfo propertyInfo) + throw new ArgumentException( + $"Expression '{propertyExpression}' must refer to a property, not a field/method", + nameof(propertyExpression)); + + return propertyInfo; + } +} diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs index ae4ede163..429b4e785 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs @@ -5,7 +5,8 @@ namespace ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; public class CycleFormatterStrategy : IPrintStrategy { private readonly Dictionary visited = new(); - public bool CanHandle(Type type) => true; + public bool CanHandle(Type type) => !type.IsValueType && type != typeof(string); + public string Print(object obj, int nestingLevel, HashSet ignoredVisited, Func, string> ignoredRecursivePrinter) diff --git a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs index 28d1a3721..e69e79fa7 100644 --- a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs @@ -105,7 +105,7 @@ public static IEnumerable TestCases () => { var p = new Person { Height = 12.5 }; - var pr = ObjectPrinter.For().SetNumericCulture(CultureInfo.InvariantCulture); + var pr = ObjectPrinter.For().SetFormattingCulture(CultureInfo.InvariantCulture); return pr.PrintToString(p); }, shouldContain: ["12.5"] @@ -254,7 +254,7 @@ public static IEnumerable TestCases () => { var p = new Person { Salary = 1234.567m }; - var pr = ObjectPrinter.For().SetNumericCulture(CultureInfo.InvariantCulture); + var pr = ObjectPrinter.For().SetFormattingCulture(CultureInfo.InvariantCulture); return pr.PrintToString(p); }, shouldContain: ["1234.567"] From c5ec28999343cf940c837ba119f41fb5fef73e00 Mon Sep 17 00:00:00 2001 From: younggogy Date: Sun, 23 Nov 2025 19:04:56 +0500 Subject: [PATCH 13/14] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=B2=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20=D1=81=20NRE,?= =?UTF-8?q?=20=D1=83=D0=B1=D1=80=D0=B0=D0=BB=20=D0=B1=D0=B8=D0=BB=D0=B4?= =?UTF-8?q?=D0=B5=D1=80=20=D0=B2=20=D1=80=D0=B5=D0=BA=D1=83=D1=80=D1=81?= =?UTF-8?q?=D0=B8=D0=B2=D0=BD=D1=8B=D0=B9=20=D0=BF=D1=80=D0=B8=D0=BD=D1=82?= =?UTF-8?q?=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Implementations/PrintingProcessor.cs | 4 +++- .../Implementations/CycleFormatterStrategy.cs | 3 ++- .../Implementations/EnumerablePrinterStrategy.cs | 7 +++---- .../Implementations/ObjectPrinterStrategy.cs | 14 +++++++------- .../Implementations/SimplePrinterStrategy.cs | 5 +++-- .../Strategies/Interfaces/IPrintStrategy.cs | 4 +++- .../Strategies/Implementations/CultureRule.cs | 4 ++-- .../Strategies/Implementations/ExcludeRule.cs | 4 ++-- .../Implementations/SerializationRule.cs | 2 +- .../Strategies/Implementations/TrimStringRule.cs | 2 +- .../Strategies/Interfaces/ISerializationRule.cs | 2 +- 11 files changed, 28 insertions(+), 23 deletions(-) diff --git a/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs index 9ea4d56ae..e47a6813d 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs @@ -1,3 +1,4 @@ +using System.Text; using ObjectPrinting.HomeWork.PrintUtils.Interfaces; using ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; using ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; @@ -16,11 +17,12 @@ public string Print(object? obj, int nestingLevel, HashSet visited) var type = obj.GetType(); + var sb = new StringBuilder(); foreach (var strategy in strategies.OrderByDescending(strategy => strategy is CycleFormatterStrategy)) { if (!strategy.CanHandle(type)) continue; - var result = strategy.Print(obj, nestingLevel, visited, Print); + var result = strategy.Print(obj, nestingLevel, visited, Print, sb); if (result != null) { return result; diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs index 429b4e785..ffac456d7 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs @@ -1,3 +1,4 @@ +using System.Text; using ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; namespace ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; @@ -9,7 +10,7 @@ public class CycleFormatterStrategy : IPrintStrategy public string Print(object obj, int nestingLevel, HashSet ignoredVisited, - Func, string> ignoredRecursivePrinter) + Func, string> ignoredRecursivePrinter, StringBuilder sb) { if (visited.TryGetValue(obj, out var originalLevel)) { diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/EnumerablePrinterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/EnumerablePrinterStrategy.cs index e29ceb318..d702f0ec3 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/EnumerablePrinterStrategy.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/EnumerablePrinterStrategy.cs @@ -9,16 +9,15 @@ public class EnumerablePrinterStrategy : IPrintStrategy public bool CanHandle(Type type) => typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string); public string Print(object obj, int nestingLevel, HashSet visited, - Func, string> recursivePrinter) + Func, string> recursivePrinter, StringBuilder sb) { - return PrintEnumerable((IEnumerable)obj, nestingLevel, visited, recursivePrinter); + return PrintEnumerable((IEnumerable)obj, nestingLevel, visited, recursivePrinter, sb); } private string PrintEnumerable(IEnumerable enumerable, int nestingLevel, HashSet visited, - Func, string> recursivePrinter) + Func, string> recursivePrinter, StringBuilder sb) { var indent = new string('\t', nestingLevel); - var sb = new StringBuilder(); sb.AppendLine(indent + enumerable.GetType().Name + " ["); diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs index f0295ae0d..6f9dc5c30 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs @@ -13,9 +13,9 @@ public class ObjectPrinterStrategy(IPropertyRenderer propertyRenderer, IRuleProc public bool CanHandle(Type type) => !typeof(IEnumerable).IsAssignableFrom(type) && !SimpleHelper.IsSimple(type); public string Print(object obj, int nestingLevel, HashSet visited, - Func, string> recursivePrinter) + Func, string> recursivePrinter, StringBuilder sb) { - return PrintObject(obj, nestingLevel, visited, recursivePrinter, ruleProcessor, propertyRenderer); + return PrintObject(obj, nestingLevel, visited, recursivePrinter, ruleProcessor, propertyRenderer, sb); } private string PrintObject( @@ -24,11 +24,11 @@ private string PrintObject( HashSet visited, Func, string> recursivePrinter, IRuleProcessor ruleProcessor, - IPropertyRenderer propertyRenderer) + IPropertyRenderer propertyRenderer, + StringBuilder sb) { var type = obj.GetType(); - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine(type.Name); + sb.AppendLine(type.Name); var indent = new string('\t', nestingLevel + 1); foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) @@ -40,10 +40,10 @@ private string PrintObject( if (!string.IsNullOrEmpty(propLine)) { - stringBuilder.AppendLine(indent + propLine); + sb.AppendLine(indent + propLine); } } - return stringBuilder.ToString().TrimEnd(); + return sb.ToString().TrimEnd(); } } \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs index 70c9ec6bc..41e250395 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Text; using ObjectPrinting.HomeWork.PrintUtils.Helpers; using ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; using ObjectPrinting.HomeWork.RuleUtils.Dto; @@ -11,7 +12,7 @@ public class SimplePrinterStrategy(IRuleProcessor ruleProcessor) : IPrintStrateg public bool CanHandle(Type type) => SimpleHelper.IsSimple(type); public string Print(object obj, int nestingLevel, HashSet visited, - Func, string> recursivePrinter) + Func, string> recursivePrinter, StringBuilder sb) { return Format(obj); } @@ -21,7 +22,7 @@ private string Format(object obj, PropertyInfo? propInfo = null) var outcome = ruleProcessor.ApplyRule(obj, propInfo); if (outcome.Action == RuleResult.Skip) { - return "null"; + return null; } return outcome.Value ?? obj.ToString() ?? string.Empty; } diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Interfaces/IPrintStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Interfaces/IPrintStrategy.cs index 469e94de3..c688c971f 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Interfaces/IPrintStrategy.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Interfaces/IPrintStrategy.cs @@ -1,7 +1,9 @@ +using System.Text; + namespace ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; public interface IPrintStrategy { bool CanHandle(Type type); - string Print(object obj, int nestingLevel, HashSet visited, Func, string> recursivePrinter); + string Print(object obj, int nestingLevel, HashSet visited, Func, string> recursivePrinter, StringBuilder sb); } \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs index 3dcc745ca..423ae3a2e 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs @@ -7,9 +7,9 @@ namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; public class CultureRule(CultureInfo cultureInfo) : ISerializationRule { - public bool CanApply(PropertyInfo propertyInfo) + public bool CanApply(PropertyInfo? propertyInfo) { - return typeof(IFormattable).IsAssignableFrom(propertyInfo.PropertyType); + return propertyInfo != null && typeof(IFormattable).IsAssignableFrom(propertyInfo.PropertyType); } public RuleOutcome Apply(object value) diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs index 5daceaf5b..bdcdc3119 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs @@ -19,13 +19,13 @@ public ExcludeRule(PropertyInfo property) propertyToExclude = property; } - public bool CanApply(PropertyInfo propertyInfo) + public bool CanApply(PropertyInfo? propertyInfo) { if (propertyToExclude != null) return propertyInfo == propertyToExclude; if (typeToExclude != null) - return propertyInfo.PropertyType == typeToExclude; + return propertyInfo != null && propertyInfo.PropertyType == typeToExclude; return false; } diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs index 4e7cb0812..156ef1020 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs @@ -6,7 +6,7 @@ namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; public class SerializationRule(Func serializer, PropertyInfo? property = null) : ISerializationRule { - public bool CanApply(PropertyInfo propertyInfo) + public bool CanApply(PropertyInfo? propertyInfo) { if (propertyInfo is null) return true; var rightValue = propertyInfo.PropertyType == typeof(T); diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs index 7a9183281..2ae7f5e06 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs @@ -6,7 +6,7 @@ namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; public class TrimStringRule(PropertyInfo property, int length) : ISerializationRule { - public bool CanApply(PropertyInfo propertyInfo) + public bool CanApply(PropertyInfo? propertyInfo) { return property == propertyInfo && propertyInfo.PropertyType == typeof(string); } diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs index 28c3664f9..0d8c58225 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs @@ -5,6 +5,6 @@ namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; public interface ISerializationRule { - bool CanApply(PropertyInfo propertyInfo); + bool CanApply(PropertyInfo? propertyInfo); RuleOutcome Apply(object value); } \ No newline at end of file From 561217301f4a609912a07699b2d66b8d0cd41442 Mon Sep 17 00:00:00 2001 From: younggogy Date: Sun, 23 Nov 2025 20:08:54 +0500 Subject: [PATCH 14/14] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B0=D0=BB=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D1=83=20flu?= =?UTF-8?q?entapi=20=D0=B4=D0=BB=D1=8F=20=D0=B1=D0=BE=D0=BB=D0=B5=D0=B5=20?= =?UTF-8?q?=D1=83=D0=B4=D0=BE=D0=B1=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B8=20?= =?UTF-8?q?=D0=B3=D0=B8=D0=B1=D0=BA=D0=BE=D0=B3=D0=BE=20=D0=B8=D1=81=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F,?= =?UTF-8?q?=20=D0=B4=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BB=20=D0=BF=D0=B0?= =?UTF-8?q?=D1=80=D1=83=20=D0=BD=D0=BE=D0=B2=D1=8B=D1=85=20=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{SimpleHelper.cs => SimpleTypeHelper.cs} | 2 +- .../Implementations/PropertyRenderer.cs | 2 +- .../HomeWork/PrintUtils/PrintingConfig.cs | 61 +++++-------- .../PrintUtils/PropertyPrintingConfig.cs | 40 ++++++++ .../Implementations/ObjectPrinterStrategy.cs | 2 +- .../Implementations/SimplePrinterStrategy.cs | 2 +- .../HomeWork/PrintUtils/TypePrintingConfig.cs | 38 ++++++++ .../Strategies/Implementations/CultureRule.cs | 13 ++- .../Implementations/TrimStringRule.cs | 30 +++++- .../Tests/ObjectPrinterAcceptanceTests.cs | 91 +++++++++++++++++++ 10 files changed, 235 insertions(+), 46 deletions(-) rename ObjectPrinting/HomeWork/PrintUtils/Helpers/{SimpleHelper.cs => SimpleTypeHelper.cs} (92%) create mode 100644 ObjectPrinting/HomeWork/PrintUtils/PropertyPrintingConfig.cs create mode 100644 ObjectPrinting/HomeWork/PrintUtils/TypePrintingConfig.cs diff --git a/ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleHelper.cs b/ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleTypeHelper.cs similarity index 92% rename from ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleHelper.cs rename to ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleTypeHelper.cs index 2081cf7e3..77ee533d9 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleHelper.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleTypeHelper.cs @@ -1,6 +1,6 @@ namespace ObjectPrinting.HomeWork.PrintUtils.Helpers; -public static class SimpleHelper +public static class SimpleTypeHelper { public static bool IsSimple(Type type) { diff --git a/ObjectPrinting/HomeWork/PrintUtils/Implementations/PropertyRenderer.cs b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PropertyRenderer.cs index ad76d2d81..77e53718c 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Implementations/PropertyRenderer.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PropertyRenderer.cs @@ -25,7 +25,7 @@ public class PropertyRenderer : IPropertyRenderer var type = value.GetType(); - if (typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string) || !SimpleHelper.IsSimple(type)) + if (typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string) || !SimpleTypeHelper.IsSimple(type)) { var printed = recursivePrinter(value, nestingLevel + 1, visited); return $"{prop.Name} =\n{printed}"; diff --git a/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs index aa4aa6d84..814060769 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs @@ -7,67 +7,54 @@ namespace ObjectPrinting.HomeWork.PrintUtils; -public class PrintingConfig(IRuleProcessor ruleProcessor, IPrintingProcessor printingProcessor) +public class PrintingConfig(IRuleProcessor rp, IPrintingProcessor pr) { - public string PrintToString(TOwner obj) - { - return printingProcessor.Print(obj, 0, []); - } + public string PrintToString(TOwner obj) => pr.Print(obj, 0, []); + public TypePrintingConfig For() => new(rp, this); + public PropertyPrintingConfig For(Expression> selector) + => new(rp, this, selector); + public PrintingConfig Exclude() { - ruleProcessor.AddRule(new ExcludeRule(typeof(T))); + rp.AddRule(new ExcludeRule(typeof(T))); return this; } - public PrintingConfig SetFormattingCulture(CultureInfo culture) + public PrintingConfig Exclude(Expression> selector) { - ruleProcessor.AddRule(new CultureRule(culture)); + var prop = GetProperty(selector); + rp.AddRule(new ExcludeRule(prop)); return this; } - public PrintingConfig Serialize(Func serializer) + public PrintingConfig Trim(Expression> selector, int length) { - ruleProcessor.AddRule(new SerializationRule(serializer)); + var prop = GetProperty(selector); + rp.AddRule(new TrimStringRule(prop, length)); return this; } - public PrintingConfig Serialize( - Expression> property, - Func serializer) + public PrintingConfig Serialize(Func serializer) { - var propertyInfo = GetPropertyInfo(property); - ruleProcessor.AddRule(new SerializationRule(serializer, propertyInfo)); + rp.AddRule(new SerializationRule(serializer)); return this; } - public PrintingConfig Trim(Expression> property, int length) + public PrintingConfig Serialize(Expression> selector, + Func serializer) { - var propertyInfo = GetPropertyInfo(property); - ruleProcessor.AddRule(new TrimStringRule(propertyInfo, length)); + var prop = GetProperty(selector); + rp.AddRule(new SerializationRule(serializer, prop)); return this; } - public PrintingConfig Exclude(Expression> property) + public PrintingConfig SetFormattingCulture(CultureInfo culture) { - var propertyInfo = GetPropertyInfo(property); - ruleProcessor.AddRule(new ExcludeRule(propertyInfo)); + rp.AddRule(new CultureRule(culture)); return this; } - - private static PropertyInfo GetPropertyInfo( - Expression> propertyExpression) - { - if (propertyExpression.Body is not MemberExpression memberExpr) - throw new ArgumentException( - $"Expression '{propertyExpression}' must be a simple property access", - nameof(propertyExpression)); - if (memberExpr.Member is not PropertyInfo propertyInfo) - throw new ArgumentException( - $"Expression '{propertyExpression}' must refer to a property, not a field/method", - nameof(propertyExpression)); - - return propertyInfo; - } -} + private static PropertyInfo GetProperty(Expression> expr) + => (PropertyInfo)((MemberExpression)expr.Body).Member; +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/PropertyPrintingConfig.cs b/ObjectPrinting/HomeWork/PrintUtils/PropertyPrintingConfig.cs new file mode 100644 index 000000000..548dd3a75 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/PropertyPrintingConfig.cs @@ -0,0 +1,40 @@ +using System.Linq.Expressions; +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; + +namespace ObjectPrinting.HomeWork.PrintUtils; + +public class PropertyPrintingConfig( + IRuleProcessor rules, + PrintingConfig parent, + Expression> selector) +{ + private readonly PropertyInfo property = GetProperty(selector); + + public PropertyPrintingConfig Serialize(Func serializer) + { + rules.AddRule(new SerializationRule(serializer, property)); + return this; + } + + public PropertyPrintingConfig Trim(int length) + { + if (typeof(TProp) != typeof(string)) + throw new InvalidOperationException("Trim доступен только для строк."); + + rules.AddRule(new TrimStringRule(property, length)); + return (PropertyPrintingConfig)(object)this; + } + + public PropertyPrintingConfig Exclude() + { + rules.AddRule(new ExcludeRule(property)); + return this; + } + + public PrintingConfig Apply() => parent; + + private static PropertyInfo GetProperty(Expression> exp) => + (PropertyInfo)((MemberExpression)exp.Body).Member; +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs index 6f9dc5c30..3dd156708 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs @@ -10,7 +10,7 @@ namespace ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; public class ObjectPrinterStrategy(IPropertyRenderer propertyRenderer, IRuleProcessor ruleProcessor) : IPrintStrategy { - public bool CanHandle(Type type) => !typeof(IEnumerable).IsAssignableFrom(type) && !SimpleHelper.IsSimple(type); + public bool CanHandle(Type type) => !typeof(IEnumerable).IsAssignableFrom(type) && !SimpleTypeHelper.IsSimple(type); public string Print(object obj, int nestingLevel, HashSet visited, Func, string> recursivePrinter, StringBuilder sb) diff --git a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs index 41e250395..2547f1cd5 100644 --- a/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs @@ -9,7 +9,7 @@ namespace ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; public class SimplePrinterStrategy(IRuleProcessor ruleProcessor) : IPrintStrategy { - public bool CanHandle(Type type) => SimpleHelper.IsSimple(type); + public bool CanHandle(Type type) => SimpleTypeHelper.IsSimple(type); public string Print(object obj, int nestingLevel, HashSet visited, Func, string> recursivePrinter, StringBuilder sb) diff --git a/ObjectPrinting/HomeWork/PrintUtils/TypePrintingConfig.cs b/ObjectPrinting/HomeWork/PrintUtils/TypePrintingConfig.cs new file mode 100644 index 000000000..e567e3890 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/TypePrintingConfig.cs @@ -0,0 +1,38 @@ +using System.Globalization; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; + +namespace ObjectPrinting.HomeWork.PrintUtils; + +public class TypePrintingConfig(IRuleProcessor rules, PrintingConfig parent) +{ + public TypePrintingConfig SetCulture(CultureInfo culture) + { + rules.AddRule(new CultureRule(culture, typeof(TType))); + return this; + } + + public TypePrintingConfig Serialize(Func serializer) + { + rules.AddRule(new SerializationRule(serializer)); + return this; + } + + public TypePrintingConfig Exclude() + { + rules.AddRule(new ExcludeRule(typeof(TType))); + return this; + } + + public TypePrintingConfig Trim(int length) + { + if (typeof(TType) != typeof(string)) + throw new InvalidOperationException("Trim доступен только для строк."); + + rules.AddRule(new TrimStringRule(typeof(TType), length)); + return this; + } + + + public PrintingConfig Apply() => parent; +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs index 423ae3a2e..1d4573890 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs @@ -5,11 +5,20 @@ namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; -public class CultureRule(CultureInfo cultureInfo) : ISerializationRule +public class CultureRule(CultureInfo cultureInfo, Type? targetType = null) : ISerializationRule { public bool CanApply(PropertyInfo? propertyInfo) { - return propertyInfo != null && typeof(IFormattable).IsAssignableFrom(propertyInfo.PropertyType); + if (propertyInfo == null) + return false; + + if (!typeof(IFormattable).IsAssignableFrom(propertyInfo.PropertyType)) + return false; + + if (targetType == null) + return true; + + return propertyInfo.PropertyType == targetType; } public RuleOutcome Apply(object value) diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs index 2ae7f5e06..57f0f6f4d 100644 --- a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs @@ -4,15 +4,39 @@ namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; -public class TrimStringRule(PropertyInfo property, int length) : ISerializationRule +public class TrimStringRule : ISerializationRule { + private readonly PropertyInfo? property; + private readonly Type? type; + private readonly int length; + + public TrimStringRule(PropertyInfo property, int length) + { + this.property = property; + this.length = length; + } + + public TrimStringRule(Type type, int length) + { + this.type = type; + this.length = length; + } + public bool CanApply(PropertyInfo? propertyInfo) { - return property == propertyInfo && propertyInfo.PropertyType == typeof(string); + if (property != null) + return propertyInfo == property; + + if (type != null) + return propertyInfo?.PropertyType == type; + + return false; } public RuleOutcome Apply(object value) { - return new RuleOutcome(RuleResult.Print, ((string)value)[..Math.Min(length, ((string)value).Length)]); + var s = (string)value; + var trimmed = s[..Math.Min(length, s.Length)]; + return new RuleOutcome(RuleResult.Print, trimmed); } } \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs index e69e79fa7..861afadd2 100644 --- a/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs +++ b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs @@ -32,6 +32,97 @@ public static IEnumerable TestCases { get { + yield return new TestCaseData(new TestSpec( + "Type-level exclude for string properties should remove all string fields", + () => + { + var p = new Person + { + Name = "Alex", + Note = "Some note", + Age = 30 + }; + + var pr = ObjectPrinter.For() + .For().Exclude().Apply(); + + return pr.PrintToString(p); + }, + shouldNotContain: ["Alex", "Some note"], + shouldContain: ["30"] + )).SetName("ShouldBeTrue_When_TypeExclude_RemovesAllPropertiesOfThatType"); + + yield return new TestCaseData(new TestSpec( + "Trim applied to all string properties", + () => + { + var p = new Person { Name = "Alexander", Note = "LongNote" }; + var pr = ObjectPrinter.For() + .For().Trim(3).Apply(); + + return pr.PrintToString(p); + }, + shouldContain: ["Ale", "Lon"] + )).SetName("ShouldBeTrue_When_TrimAppliedToAllStringProperties"); + + + yield return new TestCaseData(new TestSpec( + "Property serializer should override type serializer", + () => + { + var p = new Person { Age = 50 }; + var pr = ObjectPrinter.For() + .For().Serialize(i => $"T{i}").Apply() + .For(x => x.Age).Serialize(i => $"P{i}").Apply(); + + return pr.PrintToString(p); + }, + shouldContain: ["T50"], + shouldNotContain: ["P50"] + )).SetName("ShouldBeTrue_When_PropertySerializer_OverridesTypeSerializer"); + + yield return new TestCaseData(new TestSpec( + "Exclude specific property", + () => + { + var p = new Person { Name = "Bob", Note = "hidden" }; + var pr = ObjectPrinter.For() + .For(x => x.Note).Exclude().Apply(); + + return pr.PrintToString(p); + }, + shouldNotContain: ["hidden"] + )).SetName("ShouldBeTrue_When_ExcludeProperty_RemovesIt"); + + + yield return new TestCaseData(new TestSpec( + "Trim should not affect non-string properties", + () => + { + var p = new Person { Age = 12345 }; + var pr = ObjectPrinter.For() + .For(x => x.Name).Trim(2).Apply(); + + return pr.PrintToString(p); + }, + shouldContain: ["12345"] + )).SetName("ShouldBeTrue_When_TrimNotAppliedToNonString"); + + yield return new TestCaseData(new TestSpec( + "Culture for double should not affect decimal", + () => + { + var p = new Person { Height = 1234.5, Salary = 999.99m }; + var pr = ObjectPrinter.For() + .For().SetCulture(CultureInfo.InvariantCulture).Apply(); + + return pr.PrintToString(p); + }, + shouldContain: ["1234.5"], + shouldNotContain: ["999.99"] + )).SetName("ShouldBeTrue_When_CultureAppliedOnlyToDouble"); + + yield return new TestCaseData(new TestSpec( "Exclude should remove int fields", () =>