using System.Linq.Expressions; using System.Reflection; using Microsoft.EntityFrameworkCore; using Pf2e.Web.Helpers.Format; namespace Pf2e.Web.Helpers.Serialization; /// /// Построитель группы полей для типа TField. /// /// Тип сущности для группы полей. public class FieldGroupBuilder(string name) where TField : class { private readonly FieldGroupDefinition _groupDefinition = new() { Name = name, GroupType = typeof(TField), }; /// /// Добавляет поле в группу. /// /// Лямбда-выражение для получения поля. /// Текущий экземпляр построителя группы полей. public FieldGroupBuilder AddField(Expression> fieldExpression) { // Получаем информацию о свойстве из выражения MemberExpression? memberExpression = GetMemberExpression(fieldExpression.Body); if (memberExpression == null) { throw new ArgumentException( "Выражение должно быть доступом к свойству", nameof(fieldExpression) ); } PropertyInfo propertyInfo = (PropertyInfo)memberExpression.Member; // Получаем атрибут CommentAttribute, если он есть CommentAttribute? commentAttribute = propertyInfo.GetCustomAttribute(); string fieldName = commentAttribute?.Comment ?? propertyInfo.Name; Type fieldType = propertyInfo.PropertyType; _groupDefinition.Fields.Add(new() { Name = fieldName, Type = fieldType }); return this; } /// /// Устанавливает форматтер значения для последнего добавленного поля. /// /// Функция форматирования значения. /// Текущий экземпляр построителя группы полей. public FieldGroupBuilder SetValueFormatter(Func valueFormatter) { _groupDefinition.Fields.Last().ValueFormatter = valueFormatter; return this; } /// /// Использует комментарий из другого поля для установки имени последнего добавленного поля. /// /// Лямбда-выражение для получения поля с комментарием. /// Текущий экземпляр построителя группы полей. public FieldGroupBuilder UseCommentFrom( Expression> fieldExpression ) { // Получаем информацию о свойстве из выражения MemberExpression? memberExpression = GetMemberExpression(fieldExpression.Body); if (memberExpression == null) { throw new ArgumentException( "Выражение должно быть доступом к свойству", nameof(fieldExpression) ); } PropertyInfo propertyInfo = (PropertyInfo)memberExpression.Member; // Получаем атрибут CommentAttribute, если он есть CommentAttribute? commentAttribute = propertyInfo.GetCustomAttribute(); if (commentAttribute != null) { // Устанавливаем имя последнего добавленного поля if (_groupDefinition.Fields.Count > 0) { _groupDefinition.Fields.Last().Name = commentAttribute.Comment; } } return this; } /// /// Завершает построение группы полей и возвращает определение группы. /// /// Итоговое определение группы полей. public FieldGroupDefinition Build() => _groupDefinition; /// /// Получает MemberExpression из тела лямбда-выражения. /// /// Тело выражения. /// MemberExpression или null, если не удалось получить. private static MemberExpression? GetMemberExpression(Expression expression) { return expression switch { MemberExpression memberExpression => memberExpression, UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression => unaryExpression.Operand as MemberExpression, _ => null, }; } }