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 new file mode 100644 index 000000000..b04e94864 --- /dev/null +++ b/ObjectPrinting/HomeWork/ObjectPrinter.cs @@ -0,0 +1,24 @@ +using ObjectPrinting.HomeWork.PrintUtils; +using ObjectPrinting.HomeWork.PrintUtils.Implementations; +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Implementations; + +namespace ObjectPrinting.HomeWork; + +public class ObjectPrinter +{ + public static PrintingConfig For() + { + var ruleProcessor = new RuleProcessor(); + var renderProperty = new PropertyRenderer(); + 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 diff --git a/ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleTypeHelper.cs b/ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleTypeHelper.cs new file mode 100644 index 000000000..77ee533d9 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Helpers/SimpleTypeHelper.cs @@ -0,0 +1,15 @@ +namespace ObjectPrinting.HomeWork.PrintUtils.Helpers; + +public static class SimpleTypeHelper +{ + 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 diff --git a/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs new file mode 100644 index 000000000..e47a6813d --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Implementations/PrintingProcessor.cs @@ -0,0 +1,35 @@ +using System.Text; +using ObjectPrinting.HomeWork.PrintUtils.Interfaces; +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Implementations; +using ObjectPrinting.HomeWork.PrintUtils.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.PrintUtils.Implementations; + +public class PrintingProcessor( + IEnumerable strategies) + : IPrintingProcessor +{ + private readonly List strategies = strategies.ToList(); + + public string Print(object? obj, int nestingLevel, HashSet visited) + { + if (obj == null) return "null"; + + 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, sb); + if (result != null) + { + return result; + } + } + + throw new InvalidOperationException($"No strategy could print object of type {type.Name}"); + } + +} \ 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..77e53718c --- /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) || !SimpleTypeHelper.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/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/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/PrintingConfig.cs b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs new file mode 100644 index 000000000..814060769 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/PrintingConfig.cs @@ -0,0 +1,60 @@ +using System.Globalization; +using System.Linq.Expressions; +using System.Reflection; +using ObjectPrinting.HomeWork.PrintUtils.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; + +namespace ObjectPrinting.HomeWork.PrintUtils; + +public class PrintingConfig(IRuleProcessor rp, IPrintingProcessor pr) +{ + 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() + { + rp.AddRule(new ExcludeRule(typeof(T))); + return this; + } + + public PrintingConfig Exclude(Expression> selector) + { + var prop = GetProperty(selector); + rp.AddRule(new ExcludeRule(prop)); + return this; + } + + public PrintingConfig Trim(Expression> selector, int length) + { + var prop = GetProperty(selector); + rp.AddRule(new TrimStringRule(prop, length)); + return this; + } + + public PrintingConfig Serialize(Func serializer) + { + rp.AddRule(new SerializationRule(serializer)); + return this; + } + + public PrintingConfig Serialize(Expression> selector, + Func serializer) + { + var prop = GetProperty(selector); + rp.AddRule(new SerializationRule(serializer, prop)); + return this; + } + + public PrintingConfig SetFormattingCulture(CultureInfo culture) + { + rp.AddRule(new CultureRule(culture)); + return this; + } + + 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/CycleFormatterStrategy.cs b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs new file mode 100644 index 000000000..ffac456d7 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/CycleFormatterStrategy.cs @@ -0,0 +1,30 @@ +using System.Text; +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) => !type.IsValueType && type != typeof(string); + + + public string Print(object obj, int nestingLevel, HashSet ignoredVisited, + Func, string> ignoredRecursivePrinter, StringBuilder sb) + { + 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..d702f0ec3 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/EnumerablePrinterStrategy.cs @@ -0,0 +1,38 @@ +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, StringBuilder sb) + { + return PrintEnumerable((IEnumerable)obj, nestingLevel, visited, recursivePrinter, sb); + } + + private string PrintEnumerable(IEnumerable enumerable, int nestingLevel, HashSet visited, + Func, string> recursivePrinter, StringBuilder sb) + { + var indent = new string('\t', nestingLevel); + + 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..3dd156708 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/ObjectPrinterStrategy.cs @@ -0,0 +1,49 @@ +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) && !SimpleTypeHelper.IsSimple(type); + + public string Print(object obj, int nestingLevel, HashSet visited, + Func, string> recursivePrinter, StringBuilder sb) + { + return PrintObject(obj, nestingLevel, visited, recursivePrinter, ruleProcessor, propertyRenderer, sb); + } + + private string PrintObject( + object obj, + int nestingLevel, + HashSet visited, + Func, string> recursivePrinter, + IRuleProcessor ruleProcessor, + IPropertyRenderer propertyRenderer, + StringBuilder sb) + { + var type = obj.GetType(); + sb.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)) + { + sb.AppendLine(indent + propLine); + } + } + + 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 new file mode 100644 index 000000000..2547f1cd5 --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Implementations/SimplePrinterStrategy.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Text; +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) => SimpleTypeHelper.IsSimple(type); + + public string Print(object obj, int nestingLevel, HashSet visited, + Func, string> recursivePrinter, StringBuilder sb) + { + 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..c688c971f --- /dev/null +++ b/ObjectPrinting/HomeWork/PrintUtils/Strategies/Interfaces/IPrintStrategy.cs @@ -0,0 +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, StringBuilder sb); +} \ No newline at end of file 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/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/RuleUtils/Implementations/RuleProcessor.cs b/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs new file mode 100644 index 000000000..f4ac717ef --- /dev/null +++ b/ObjectPrinting/HomeWork/RuleUtils/Implementations/RuleProcessor.cs @@ -0,0 +1,44 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.RuleUtils.Interfaces; +using ObjectPrinting.HomeWork.RuleUtils.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) + { + 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))) + { + var outcome = rule.Apply(current); + + if (outcome.Action == RuleResult.Skip) + return new RuleOutcome(RuleResult.Skip, null); + + current = outcome.Value ?? current; + resultString = outcome.Value; + } + + + return new RuleOutcome( + RuleResult.Print, + resultString ?? 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..d600afcef --- /dev/null +++ b/ObjectPrinting/HomeWork/RuleUtils/Interfaces/IRuleProcessor.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.RuleUtils.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/RuleUtils/Strategies/Implementations/CultureRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs new file mode 100644 index 000000000..1d4573890 --- /dev/null +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/CultureRule.cs @@ -0,0 +1,28 @@ +using System.Globalization; +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; + +public class CultureRule(CultureInfo cultureInfo, Type? targetType = null) : ISerializationRule +{ + public bool CanApply(PropertyInfo? propertyInfo) + { + 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) + { + return new RuleOutcome(RuleResult.Print, ((IFormattable)value).ToString(null, cultureInfo)); + } +} \ No newline at end of file diff --git a/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs new file mode 100644 index 000000000..bdcdc3119 --- /dev/null +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/ExcludeRule.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.RuleUtils.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 != null && 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/RuleUtils/Strategies/Implementations/SerializationRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs new file mode 100644 index 000000000..156ef1020 --- /dev/null +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/SerializationRule.cs @@ -0,0 +1,25 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; + +public class SerializationRule(Func serializer, PropertyInfo? property = null) : ISerializationRule +{ + public bool CanApply(PropertyInfo? propertyInfo) + { + 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) + { + 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/RuleUtils/Strategies/Implementations/TrimStringRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs new file mode 100644 index 000000000..57f0f6f4d --- /dev/null +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Implementations/TrimStringRule.cs @@ -0,0 +1,42 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; +using ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; + +namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Implementations; + +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) + { + if (property != null) + return propertyInfo == property; + + if (type != null) + return propertyInfo?.PropertyType == type; + + return false; + } + + public RuleOutcome Apply(object value) + { + 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/RuleUtils/Strategies/Interfaces/ISerializationRule.cs b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs new file mode 100644 index 000000000..0d8c58225 --- /dev/null +++ b/ObjectPrinting/HomeWork/RuleUtils/Strategies/Interfaces/ISerializationRule.cs @@ -0,0 +1,10 @@ +using System.Reflection; +using ObjectPrinting.HomeWork.RuleUtils.Dto; + +namespace ObjectPrinting.HomeWork.RuleUtils.Strategies.Interfaces; + +public interface ISerializationRule +{ + bool CanApply(PropertyInfo? propertyInfo); + RuleOutcome Apply(object value); +} \ 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..861afadd2 --- /dev/null +++ b/ObjectPrinting/HomeWork/Tests/ObjectPrinterAcceptanceTests.cs @@ -0,0 +1,481 @@ +using System.Globalization; +using FluentAssertions; +using NUnit.Framework; +using ObjectPrinting.HomeWork.Extensions; + +namespace ObjectPrinting.HomeWork.Tests +{ + [TestFixture] + public class ObjectPrinterGeneratedTests + { + 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 + { + 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", + () => + { + var p = new Person { Name = "A", Age = 10 }; + var pr = ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.For().SetFormattingCulture(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 = ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.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 ObjectPrinter.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 ObjectPrinter.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 ObjectPrinter.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 ObjectPrinter.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 ObjectPrinter.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 ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.For().SetFormattingCulture(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 = ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.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 ObjectPrinter.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 ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.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 = ObjectPrinter.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 new file mode 100644 index 000000000..2c76adb0e --- /dev/null +++ b/ObjectPrinting/HomeWork/Tests/Person.cs @@ -0,0 +1,16 @@ +namespace ObjectPrinting.HomeWork.Tests +{ + public class Person + { + public string Name { 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 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/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 diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs deleted file mode 100644 index 4c8b2445c..000000000 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NUnit.Framework; - -namespace ObjectPrinting.Tests -{ - [TestFixture] - public class ObjectPrinterAcceptanceTests - { - [Test] - public void Demo() - { - var person = new Person { Name = "Alex", Age = 19 }; - - var printer = ObjectPrinter.For(); - //1. Исключить из сериализации свойства определенного типа - //2. Указать альтернативный способ сериализации для определенного типа - //3. Для числовых типов указать культуру - //4. Настроить сериализацию конкретного свойства - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - //6. Исключить из сериализации конкретного свойства - - string s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - //8. ...с конфигурированием - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting/Tests/Person.cs deleted file mode 100644 index f95559554..000000000 --- a/ObjectPrinting/Tests/Person.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ObjectPrinting.Tests -{ - public class Person - { - public Guid Id { get; set; } - public string Name { get; set; } - public double Height { get; set; } - public int Age { get; set; } - } -} \ No newline at end of file diff --git a/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