Skip to content
Open
17 changes: 17 additions & 0 deletions ObjectPrinting/ObjectPrinterExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;

namespace ObjectPrinting;

public static class ObjectPrinterExtensions
{
public static string PrintToString<T>(this T obj)
{
return ObjectPrinter.For<T>().PrintToString(obj);
}

public static string PrintToString<T>(this T obj, Func<PrintingConfig<T>, PrintingConfig<T>> configBuilder)
{
var config = configBuilder(ObjectPrinter.For<T>());
return config.PrintToString(obj);
}
}
3 changes: 2 additions & 1 deletion ObjectPrinting/ObjectPrinting.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="8.8.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>
</Project>
371 changes: 356 additions & 15 deletions ObjectPrinting/PrintingConfig.cs

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions ObjectPrinting/PropertyPath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace ObjectPrinting;

public record PropertyPath
{
private List<string> pathSegments;
public PropertyPath(List<string> pathSegments) => this.pathSegments = pathSegments;

public override string ToString() => string.Join(".", pathSegments);

public static PropertyPath ConvertFromSelector<TOwner, TPropType>(Expression<Func<TOwner, TPropType>> selector)
{
var segments = new List<string>();
var expression = selector.Body;

while (expression is MemberExpression member)
{
segments.Add(member.Member.Name);
expression = member.Expression;
}
segments.Add(typeof(TOwner).Name);

segments.Reverse();
return new PropertyPath(segments);
}
}
50 changes: 50 additions & 0 deletions ObjectPrinting/PropertyPrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Linq.Expressions;

namespace ObjectPrinting;

internal interface IPropertyPrintingConfigAccessor<TOwner, TPropType>
{
PrintingConfig<TOwner> ParentConfig { get; }
Expression<Func<TOwner, TPropType>>? PropertySelector { get; }
PropertyPath PropertyPath { get; }
}

public class PropertyPrintingConfig<TOwner, TPropType> : IPropertyPrintingConfigAccessor<TOwner, TPropType>
{
private readonly PrintingConfig<TOwner> config;
private readonly Expression<Func<TOwner, TPropType>>? selector;
private readonly PropertyPath propertyPath;

public PropertyPrintingConfig(PrintingConfig<TOwner> config, Expression<Func<TOwner, TPropType>>? selector)
{
this.config = config;
this.selector = ValidateExpression(selector);
this.propertyPath = PropertyPath.ConvertFromSelector(selector!); // Не null т.к. валидируем это
}

private Expression<Func<TOwner, TPropType>> ValidateExpression(Expression<Func<TOwner, TPropType>>? validatingSelector)
{
if (validatingSelector == null)
throw new ArgumentNullException(nameof(validatingSelector));
if (validatingSelector.Body is not MemberExpression memberExpression)
throw new ArgumentException($"{nameof(validatingSelector)} must select property or field");
if (!IsDirectPropertyAccess(memberExpression, validatingSelector.Parameters[0]))
throw new ArgumentException("Property selector should only access properties of the parameter, not external variables");
return validatingSelector;
}

private bool IsDirectPropertyAccess(Expression? expression, ParameterExpression parameter)
{
while (expression is MemberExpression member)
expression = member.Expression;
return expression == parameter;
}

PrintingConfig<TOwner> IPropertyPrintingConfigAccessor<TOwner, TPropType>.ParentConfig => config;

Expression<Func<TOwner, TPropType>>? IPropertyPrintingConfigAccessor<TOwner, TPropType>.PropertySelector => selector;

PropertyPath IPropertyPrintingConfigAccessor<TOwner, TPropType>.PropertyPath =>
propertyPath;
}
29 changes: 29 additions & 0 deletions ObjectPrinting/PropertyPrintingConfigExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;

namespace ObjectPrinting;

public static class PropertyPrintingConfigExtensions
{
public static PrintingConfig<TOwner> SetSerializationStyle<TOwner, TPropType>
(this PropertyPrintingConfig<TOwner, TPropType> propertyConfig, PrintingConfig<TOwner>.SerializerDelegate serializer)
{
var accessor = (IPropertyPrintingConfigAccessor<TOwner, TPropType>)propertyConfig;
var propertyName = PropertyPath.ConvertFromSelector(accessor.PropertySelector!);
return accessor.ParentConfig.WithPropertySerialization(propertyName, serializer);
}

public static PrintingConfig<TOwner> TrimStringToLength<TOwner>
(this PropertyPrintingConfig<TOwner, string> propertyConfig, int length)
{
var accessor = (IPropertyPrintingConfigAccessor<TOwner, string>)propertyConfig;
return accessor.ParentConfig.TrimStringToLength(accessor.PropertySelector!, length);
}

public static PrintingConfig<TOwner> Exclude<TOwner, TPropType>
(this PropertyPrintingConfig<TOwner, TPropType> propertyConfig)
{
var accessor = (IPropertyPrintingConfigAccessor<TOwner, TPropType>)propertyConfig;
var propertyName = PropertyPath.ConvertFromSelector(accessor.PropertySelector!);
return accessor.ParentConfig.Exclude(propertyName);
}
}
Loading