NeVeSpl / NTypewriter

File/code generator using Scriban text templates populated with C# code metadata from Roslyn API.
https://nevespl.github.io/NTypewriter/
MIT License
126 stars 25 forks source link

Enum Attribute arguments are stored as Type Object #85

Closed xumix closed 1 year ago

xumix commented 1 year ago

Which leads to problems like inability to get the enum value name when working with Attribute arguments. I think that better alternative is to store them as IEnumValue so that it is possible to get useful info from the value.

NeVeSpl commented 1 year ago

Do you mean an enum type used as an attribute argument? It would be nice to see c# code snippet to remove any chance for misunderstanding.

xumix commented 1 year ago

Do you mean an enum type used as an attribute argument? It would be nice to see c# code snippet to remove any chance for misunderstanding.

    [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
    public class StatisticsGroupAttribute : Attribute
    {
        protected StatisticsGroupAttribute(StatisticsGroupTypeEnum type, params object[] values)
        {
            var ids = new List<Guid>();

            Type = type;

            foreach (var value in values)
            {
                if (value is Enum @enum)
                {
                    ids.Add(@enum.GetId());
                }
                else
                {
                    throw new ArgumentException($"'{value}' не является '{nameof(Enum)}'", nameof(value));
                }
            }

            Values = ids;
        }

        public StatisticsGroupAttribute(StatisticsGroupTypeEnum type, params RegionStatTypeEnum[] values)
            : this(type, values.Cast<object>().ToArray())
        {
        }

        public StatisticsGroupAttribute(StatisticsGroupTypeEnum type, params FederalLawEnum[] values)
            : this(type, values.Cast<object>().ToArray())
        {
        }

        public StatisticsGroupTypeEnum Type { get; set; }

        public IEnumerable<Guid> Values { get; }
    }

Usage:

    public enum RegionStatTypeEnum
    {
        [Display(Name = "Число контрактов по 44-ФЗ, заключенных по результатам КС")]
        [StatisticsGroup(StatisticsGroupTypeEnum.Restriction, FederalLawEnum.FZ44)]
        AuctionContractCount44,

        [Display(Name = "Сумма по всем контрактам")]
        [StatisticsGroup(StatisticsGroupTypeEnum.Variation, ContractSum223, ContractSum44)]
        ContractSum,
}

When I try to get the StatisticsGroupAttribute Arguments I get type object containing int as a value

var args = enumValue.Attributes.First(f => f.Name == "StatisticsGroup").Arguments;

so I can't get what it the actual type of the argument

NeVeSpl commented 1 year ago

I have made a unit test:

input code:

namespace NTypewriter.Tests.CodeModel
{
    public enum RegionStatTypeEnum
    {        
        [StatisticsGroup(StatisticsGroupTypeEnum.Restriction, FederalLawEnum.FZ44)]
        AuctionContractCount44,

        [StatisticsGroup(StatisticsGroupTypeEnum.Variation, 2, FederalLawEnum.FZ69)]
        ContractSum,
    }

    [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
    public class StatisticsGroupAttribute : Attribute
    {
        public StatisticsGroupAttribute(StatisticsGroupTypeEnum type, params object[] values)
        {

        }       

        public StatisticsGroupAttribute(StatisticsGroupTypeEnum type, params FederalLawEnum[] values)
            : this(type, values.Cast<object>().ToArray())
        {
        }

        public StatisticsGroupTypeEnum Type { get; set; }

        public IEnumerable<Guid> Values { get; }
    }

    public enum StatisticsGroupTypeEnum
    {
        Restriction = 1,
        Variation = 3,
    }

    public enum FederalLawEnum
    {
        FZ44 = 44,
        FZ69 = 69,
    }
}

template:

{{- capture captured}}
{{- for enum in data.Enums | Symbols.WhereNamespaceStartsWith "NTypewriter.Tests.CodeModel" | Symbols.WhereNameEndsWith "RegionStatTypeEnum"}}
    {{- for enum_item in enum.Values }} 
    {{-  enum_item.Name}}
         {{- for attr in enum_item.Attributes }}
[{{ attr.Name }}] 
         {{- for arg in attr.Arguments }}
             name : {{ arg.Name }} 
             type : {{ arg.Type }}
             value : {{ arg.Value }}
             -------------
         {{~ end}}
         {{- end }}
    {{- end}}
{{ end }}
{{- end }}
{{- Save captured "result" }}

output:

AuctionContractCount44
[StatisticsGroup]
             name : type 
             type : StatisticsGroupTypeEnum
             value : StatisticsGroupTypeEnum.Restriction
             -------------

             name : values 
             type : FederalLawEnum[]
             value : [44]
             -------------
ContractSum
[StatisticsGroup]
             name : type 
             type : StatisticsGroupTypeEnum
             value : StatisticsGroupTypeEnum.Variation
             -------------

             name : values 
             type : object[]
             value : [2, 69]
             -------------

As you can see there is no problem with determining the type of argument of an attribute. The only problem that I see is inconsistency:

IEnumValue cannot be used here, because the parameters of attributes are TypedConstant, a completely different type.

NeVeSpl commented 1 year ago

NTypewriter will follow Roslyn design and return ITypedConstant for the value of the attribute argument.

In the upcoming version for the input from previous comment, the output will be:

AuctionContractCount44
[StatisticsGroup]
             name : type 
             type : StatisticsGroupTypeEnum
             value : StatisticsGroupTypeEnum.Restriction
             -------------

             name : values 
             type : FederalLawEnum[]
             value : [FederalLawEnum.FZ44]
             -------------
ContractSum
[StatisticsGroup]
             name : type 
             type : StatisticsGroupTypeEnum
             value : StatisticsGroupTypeEnum.Variation
             -------------

             name : values 
             type : object[]
             value : [2, FederalLawEnum.FZ69]
             -------------
xumix commented 1 year ago

Thank you so much, I'll try this!