/
Attributes.cs
148 lines (132 loc) · 6.16 KB
/
Attributes.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#nullable enable
using IPA.Config.Stores.Converters;
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace IPA.Config.Stores.Attributes
{
/// <summary>
/// Indicates that the generated subclass of the attribute's target should implement <see cref="INotifyPropertyChanged"/>.
/// If the type this is applied to already inherits it, this is implied.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class NotifyPropertyChangesAttribute : Attribute { }
/// <summary>
/// Causes a field or property in an object being wrapped by <see cref="GeneratedStore.Generated{T}(Config, bool)"/> to be
/// ignored during serialization and deserialization.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class IgnoreAttribute : Attribute { }
/// <summary>
/// Indicates that a field or property in an object being wrapped by <see cref="GeneratedStore.Generated{T}(Config, bool)"/>
/// that would otherwise be nullable (i.e. a reference type or a <see cref="Nullable{T}"/> type) should never be null, and the
/// member will be ignored if the deserialized value is <see langword="null"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class NonNullableAttribute : Attribute { }
/// <summary>
/// Indicates that a given field or property in an object being wrapped by <see cref="GeneratedStore.Generated{T}(Config, bool)"/>
/// should be serialized and deserialized using the provided converter instead of the default mechanism.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class UseConverterAttribute : Attribute
{
/// <summary>
/// Gets whether or not to use the default converter for the member type instead of the specified type.
/// </summary>
[MemberNotNullWhen(false, nameof(ConverterType))]
public bool UseDefaultConverterForType { get; }
/// <summary>
/// Gets the type of the converter to use.
/// </summary>
public Type? ConverterType { get; }
/// <summary>
/// Gets the target type of the converter if it is avaliable at instantiation time, otherwise
/// <see langword="null"/>.
/// </summary>
public Type? ConverterTargetType { get; }
/// <summary>
/// Gets whether or not this converter is a generic <see cref="ValueConverter{T}"/>.
/// </summary>
[MemberNotNullWhen(true, nameof(ConverterTargetType))]
public bool IsGenericConverter => ConverterTargetType is not null;
/// <summary>
/// Creates a new <see cref="UseConverterAttribute"/> specifying to use the default converter type for the target member.
/// </summary>
public UseConverterAttribute()
=> UseDefaultConverterForType = true;
/// <summary>
/// Creates a new <see cref="UseConverterAttribute"/> with a given <see cref="ConverterType"/>.
/// </summary>
/// <param name="converterType">the type to assign to <see cref="ConverterType"/></param>
public UseConverterAttribute(Type converterType)
{
if (converterType is null)
throw new ArgumentNullException(nameof(converterType));
UseDefaultConverterForType = false;
ConverterType = converterType;
if (converterType.IsValueType)
throw new ArgumentException("Type is not a value converter!");
var baseT = ConverterType.BaseType;
while (baseT != typeof(object) &&
(!baseT.IsGenericType || baseT.GetGenericTypeDefinition() != typeof(ValueConverter<>)))
baseT = baseT.BaseType;
if (baseT == typeof(object)) ConverterTargetType = null;
else ConverterTargetType = baseT.GetGenericArguments()[0];
var implInterface = ConverterType.GetInterfaces().Contains(typeof(IValueConverter));
if (ConverterTargetType == null && !implInterface) throw new ArgumentException("Type is not a value converter!");
}
}
/// <summary>
/// Specifies a name for the serialized field or property in an object being wrapped by
/// <see cref="GeneratedStore.Generated{T}(Config, bool)"/> that is different from the member name itself.
/// </summary>
/// <example>
/// <para>
/// When serializing the following object, we might get the JSON that follows.
/// <code>
/// public class PluginConfig
/// {
/// public virtual bool BooleanField { get; set; } = true;
/// }
/// </code>
/// <code>
/// {
/// "BooleanField": true
/// }
/// </code>
/// </para>
/// <para>
/// However, if we were to add a <see cref="SerializedNameAttribute"/> to that field, we would get the following.
/// <code>
/// public class PluginConfig
/// {
/// [SerializedName("bool")]
/// public virtual bool BooleanField { get; set; } = true;
/// }
/// </code>
/// <code>
/// {
/// "bool": true
/// }
/// </code>
/// </para>
/// </example>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class SerializedNameAttribute : Attribute
{
/// <summary>
/// Gets the name to replace the member name with.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Creates a new <see cref="SerializedNameAttribute"/> with the given <see cref="Name"/>.
/// </summary>
/// <param name="name">the value to assign to <see cref="Name"/></param>
public SerializedNameAttribute(string name)
{
Name = name;
}
}
}