• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SamboyCoding / Tomlet / 12454537091

22 Dec 2024 01:08PM UTC coverage: 91.859% (-0.1%) from 91.979%
12454537091

push

github

SamboyCoding
Fix issues from trimming support commit

961 of 1116 branches covered (86.11%)

1 of 1 new or added line in 1 file covered. (100.0%)

79 existing lines in 6 files now uncovered.

1907 of 2076 relevant lines covered (91.86%)

218.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.16
/Tomlet/TomlSerializationMethods.cs
1
using System;
2
using System.Collections;
3
using System.Collections.Generic;
4
using System.Diagnostics.CodeAnalysis;
5
using System.Globalization;
6
using System.Linq;
7
using System.Reflection;
8
using Tomlet.Exceptions;
9
using Tomlet.Extensions;
10
using Tomlet.Models;
11

12
namespace Tomlet;
13

14
public static class TomlSerializationMethods
15
{
16
#if MODERN_DOTNET
17
    internal const DynamicallyAccessedMemberTypes MainDeserializerAccessedMemberTypes = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicProperties;
18
#endif
19
        
20
    private static MethodInfo _stringKeyedDictionaryMethod = typeof(TomlSerializationMethods).GetMethod(nameof(StringKeyedDictionaryDeserializerFor), BindingFlags.Static | BindingFlags.NonPublic)!;
21
    private static MethodInfo _primitiveKeyedDictionaryMethod = typeof(TomlSerializationMethods).GetMethod(nameof(PrimitiveKeyedDictionaryDeserializerFor), BindingFlags.Static | BindingFlags.NonPublic)!;
22
    private static MethodInfo _genericDictionarySerializerMethod = typeof(TomlSerializationMethods).GetMethod(nameof(GenericDictionarySerializer), BindingFlags.Static | BindingFlags.NonPublic)!;
23
    private static MethodInfo _genericNullableSerializerMethod = typeof(TomlSerializationMethods).GetMethod(nameof(GenericNullableSerializer), BindingFlags.Static | BindingFlags.NonPublic)!;
24

25
    public delegate T Deserialize<out T>(TomlValue value);
26
    public delegate T ComplexDeserialize<out T>(TomlValue value, TomlSerializerOptions options);
27
    public delegate TomlValue? Serialize<in T>(T? t);
28
    public delegate TomlValue? ComplexSerialize<in T>(T? t, TomlSerializerOptions options);
29

30
    private static readonly Dictionary<Type, Delegate> Deserializers = new();
31
    private static readonly Dictionary<Type, Delegate> Serializers = new();
32

33

34
    [Attributes.ExcludeFromCodeCoverage]
35
    static TomlSerializationMethods()
36
    {
37
        //Register built-in serializers
38

39
        //String
40
        Register(s => new TomlString(s!), value => (value as TomlString)?.Value ?? value.StringValue);
41

42
        //Bool
43
        Register(TomlBoolean.ValueOf, value => (value as TomlBoolean)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlBoolean), value.GetType(), typeof(bool)));
44

45
        //Byte
46
        Register(i => new TomlLong(i), value => (byte)((value as TomlLong)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlLong), value.GetType(), typeof(byte))));
47

48
        //SByte
49
        Register(i => new TomlLong(i), value => (sbyte)((value as TomlLong)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlLong), value.GetType(), typeof(sbyte))));
50

51
        //UShort
52
        Register(i => new TomlLong(i), value => (ushort)((value as TomlLong)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlLong), value.GetType(), typeof(ushort))));
53

54
        //Short
55
        Register(i => new TomlLong(i), value => (short)((value as TomlLong)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlLong), value.GetType(), typeof(short))));
56

57
        //UInt
58
        Register(i => new TomlLong(i), value => (uint)((value as TomlLong)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlLong), value.GetType(), typeof(uint))));
59

60
        //Int
61
        Register(i => new TomlLong(i), value => (int)((value as TomlLong)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlLong), value.GetType(), typeof(int))));
62

63
        //ULong
64
        Register(l => new TomlLong((long)l), value => (ulong)((value as TomlLong)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlLong), value.GetType(), typeof(ulong))));
65

66
        //Long
67
        Register(l => new TomlLong(l), value => (value as TomlLong)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlLong), value.GetType(), typeof(long)));
68

69
        //Double
70
        Register(d => new TomlDouble(d), value => (value as TomlDouble)?.Value ?? (value as TomlLong)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlDouble), value.GetType(), typeof(double)));
71

72
        //Float
73
        Register(f => new TomlDouble(f), value => (float)((value as TomlDouble)?.Value ?? (value as TomlLong)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlDouble), value.GetType(), typeof(float))));
74

75
        //LocalDate(Time)
76
        Register(dt => dt.TimeOfDay == TimeSpan.Zero ? new TomlLocalDate(dt) : new TomlLocalDateTime(dt), value => (value as ITomlValueWithDateTime)?.Value ?? throw new TomlTypeMismatchException(typeof(ITomlValueWithDateTime), value.GetType(), typeof(DateTime)));
77

78
        //OffsetDateTime
79
        Register(odt => new TomlOffsetDateTime(odt), value => (value as TomlOffsetDateTime)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlOffsetDateTime), value.GetType(), typeof(DateTimeOffset)));
80

81
        //LocalTime
82
        Register(lt => new TomlLocalTime(lt), value => (value as TomlLocalTime)?.Value ?? throw new TomlTypeMismatchException(typeof(TomlLocalTime), value.GetType(), typeof(TimeSpan)));
83
    }
84

85
    /// <summary>
86
    /// Returns the default (reflection-based) serializer for the given type. Can be useful if you're implementing your own custom serializer but want to use the default behavior (e.g. to extend it or to use it as a fallback). 
87
    /// </summary>
88
    /// <param name="type">The type to get the default serializer for</param>
89
    /// <param name="options">The options to use for the serializer</param>
90
    /// <returns>The default reflection-based serializer for the given type.</returns>
91
    /// <exception cref="ArgumentException">Thrown if <paramref name="type"/> is a primitive type.</exception>
92
#if MODERN_DOTNET
93
    public static Serialize<object> GetDefaultSerializerForType([DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] Type type, TomlSerializerOptions? options = null)
94
#else
95
    public static Serialize<object> GetDefaultSerializerForType(Type type, TomlSerializerOptions? options = null)
96
#endif
UNCOV
97
    {
×
UNCOV
98
        options ??= TomlSerializerOptions.Default;
×
UNCOV
99
        if(type.IsPrimitive)
×
UNCOV
100
            throw new ArgumentException("Can't use reflection-based serializer for primitive types.", nameof(type));
×
101
            
UNCOV
102
        return TomlCompositeSerializer.For(type, options);
×
UNCOV
103
    }
×
104
        
105
    /// <summary>
106
    /// Returns the default (reflection-based) deserializer for the given type. Can be useful if you're implementing your own custom deserializer but want to use the default behavior (e.g. to extend it or to use it as a fallback).
107
    /// </summary>
108
    /// <param name="type">The type to get the default deserializer for</param>
109
    /// <param name="options">The options to use for the deserializer</param>
110
    /// <returns>The default reflection-based deserializer for the given type.</returns>
111
    /// <exception cref="ArgumentException">Thrown if <paramref name="type"/> is a primitive type.</exception>
112
#if MODERN_DOTNET
113
    public static Deserialize<object> GetDefaultDeserializerForType([DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] Type type, TomlSerializerOptions? options = null)
114
#else
115
    public static Deserialize<object> GetDefaultDeserializerForType(Type type, TomlSerializerOptions? options = null)
116
#endif
UNCOV
117
    {
×
UNCOV
118
        options ??= TomlSerializerOptions.Default;
×
UNCOV
119
        if(type.IsPrimitive)
×
UNCOV
120
            throw new ArgumentException("Can't use reflection-based deserializer for primitive types.", nameof(type));
×
121
            
UNCOV
122
        return TomlCompositeDeserializer.For(type, options);
×
UNCOV
123
    }
×
124

125
#if MODERN_DOTNET
126
#if NET7_0_OR_GREATER
127
    [RequiresDynamicCode("The native code for underlying implementations of serialize helper methods may not be available for a given type.")]
128
#endif // NET7_0_OR_GREATER
129
    internal static Serialize<object> GetSerializer([DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] Type t, TomlSerializerOptions? options)
130
#else
131
        internal static Serialize<object> GetSerializer(Type t, TomlSerializerOptions? options)
132
#endif
133
    {
257✔
134
        options ??= TomlSerializerOptions.Default;
257✔
135
            
136
        if (Serializers.TryGetValue(t, out var value))
257✔
137
            return (Serialize<object>)value;
169✔
138

139
        //First check, lists and arrays get serialized as enumerables.
140
        if (t.IsArray || t is { Namespace: "System.Collections.Generic", Name: "List`1" })
88✔
141
        {
10✔
142
            var arrSerializer = GenericEnumerableSerializer();
10✔
143
            Serializers[t] = arrSerializer;
10✔
144
            return arrSerializer;
10✔
145
        }
146

147
        //Check for dicts and nullables
148
        if (t.IsGenericType)
78✔
149
        {
17✔
150
            if (t.GetGenericTypeDefinition() == typeof(Dictionary<,>))
17✔
151
            {
5✔
152
                return DictionarySerializerFor(t, options);
5✔
153
            }
154

155
            if (t.GetGenericTypeDefinition() == typeof(Nullable<>))
12✔
156
            {
1✔
157
                return NullableSerializerFor(t, options);
1✔
158
            }
159
        }
11✔
160
            
161
        //Now we've got dicts out of the way we can check if we're dealing with something that's IEnumerable and if so serialize as an array. We do this only *after* checking for dictionaries, because we don't want to serialize dictionaries as enumerables (i.e. table-arrays)
162
        if (typeof(IEnumerable).IsAssignableFrom(t))
72✔
163
        {
1✔
164
            var enumerableSerializer = GenericEnumerableSerializer();
1✔
165
            Serializers[t] = enumerableSerializer;
1✔
166
            return enumerableSerializer;
1✔
167
        }
168

169
        return TomlCompositeSerializer.For(t, options);
71✔
170
    }
257✔
171

172
#if MODERN_DOTNET
173
    [UnconditionalSuppressMessage("AOT", "IL2060", Justification = "Any provided list, enumerable, nullable, or array type's underlying generic arguments must have been used somewhere in the consuming code in order for this method to be called, so the dynamic code requirement is already satisfied.")]
174
    [UnconditionalSuppressMessage("AOT", "IL2062", Justification = "Any provided list, enumerable, nullable, or array type's underlying generic arguments must have been used somewhere in the consuming code in order for this method to be called, so the dynamic code requirement is already satisfied.")]
175
    [UnconditionalSuppressMessage("AOT", "IL2072", Justification = "Any provided list, enumerable, nullable, or array type's underlying generic arguments must have been used somewhere in the consuming code in order for this method to be called, so the dynamic code requirement is already satisfied.")]
176
#if NET7_0_OR_GREATER        
177
    [RequiresDynamicCode("The native code for underlying implementations of deserialize helper methods may not be available for a given type.")]
178
#endif // NET7_0_OR_GREATER
179
    internal static Deserialize<object> GetDeserializer([DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] Type t, TomlSerializerOptions? options)
180
#else
181
        internal static Deserialize<object> GetDeserializer(Type t, TomlSerializerOptions? options)
182
#endif
183
    {
200✔
184
        options ??= TomlSerializerOptions.Default;
200✔
185
            
186
        if (Deserializers.TryGetValue(t, out var value))
200✔
187
            return (Deserialize<object>)value;
131✔
188

189
        //We allow deserializing to IEnumerable fields/props, by setting them an array. We DO NOT do anything for classes that implement IEnumerable, though, because that would mess with deserializing lists, dictionaries, etc.
190
        if (t.IsArray || t.IsInterface && typeof(IEnumerable).IsAssignableFrom(t)) 
69✔
191
        {
3✔
192
            var elementType = t.IsInterface ? t.GetGenericArguments()[0] : t.GetElementType()!;
3✔
193
            var arrayDeserializer = ArrayDeserializerFor(elementType, options);
3✔
194
            Deserializers[t] = arrayDeserializer;
3✔
195
            return arrayDeserializer;
3✔
196
        }
197

198
        if (t.Namespace == "System.Collections.Generic" && t.Name == "List`1")
66✔
199
        {
6✔
200
            var listDeserializer = ListDeserializerFor(t.GetGenericArguments()[0], options);
6✔
201
            Deserializers[t] = listDeserializer;
6✔
202
            return listDeserializer;
6✔
203
        }
204
            
205
        if(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>) && t.GetGenericArguments() is {Length: 1})
60!
206
        {
1✔
207
            var nullableDeserializer = NullableDeserializerFor(t, options);
1✔
208
            Deserializers[t] = nullableDeserializer;
1✔
209
            return nullableDeserializer;
1✔
210
        }
211

212
        if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dictionary<,>) && t.GetGenericArguments() is { Length: 2 } genericArgs)
59!
213
        {
5✔
214
            if (genericArgs[0] == typeof(string))
5✔
215
            {
1✔
216
                return (Deserialize<object>)_stringKeyedDictionaryMethod.MakeGenericMethod(genericArgs[1]).Invoke(null, new object[]{options})!;
1✔
217
            }
218

219
            if (genericArgs[0].IsIntegerType() || genericArgs[0] == typeof(bool) || genericArgs[0] == typeof(char))
4!
220
            {
4✔
221
                // float primitives not supported due to decimal point causing issues
222
                return (Deserialize<object>)_primitiveKeyedDictionaryMethod.MakeGenericMethod(genericArgs).Invoke(null, new object[]{options})!;
4✔
223
            }
UNCOV
224
        }
×
225

226
        return TomlCompositeDeserializer.For(t, options);
54✔
227
    }
200✔
228

229
    private static Serialize<object?> GenericEnumerableSerializer() =>
230
        o =>
11✔
231
        {
22✔
232
            if (o is not IEnumerable arr)
22!
UNCOV
233
                throw new Exception("How did ArraySerializer end up getting a non-array?");
×
234

11✔
235
            var ret = new TomlArray();
22✔
236
            foreach (var entry in arr)
176✔
237
            {
55✔
238
                ret.Add(entry);
55✔
239
            }
55✔
240

11✔
241
            return ret;
22✔
242
        };
33✔
243

244
#if MODERN_DOTNET
245
    [UnconditionalSuppressMessage("AOT", "IL2060", Justification = "The dictType's underlying generic arguments must have been used somewhere in the consuming code in order for this method to be called, so the dynamic code requirement is already satisfied.")]
246
#if NET7_0_OR_GREATER
247
    [RequiresDynamicCode("The native code for underlying implementations of serialize helper methods may not be available for a given type.")]
248
#endif // NET7_0_OR_GREATER
249
    private static Serialize<object> DictionarySerializerFor([DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] Type dictType, TomlSerializerOptions options)
250
#else
251
        private static Serialize<object> DictionarySerializerFor(Type dictType, TomlSerializerOptions options)
252
#endif
253
    {
5✔
254
        var serializer = _genericDictionarySerializerMethod.MakeGenericMethod(dictType.GetGenericArguments());
5✔
255

256
        var del = Delegate.CreateDelegate(typeof(ComplexSerialize<>).MakeGenericType(dictType), serializer);
5✔
257
        var ret = (Serialize<object>)(dict => (TomlValue?)del.DynamicInvoke(dict, options));
10✔
258
        Serializers[dictType] = ret;
5✔
259

260
        return ret;
5✔
261
    }
5✔
262
        
263
#if MODERN_DOTNET
264
    [UnconditionalSuppressMessage("AOT", "IL2060", Justification = "The nullableType's underlying T must have been used somewhere in the consuming code in order for this method to be called, so the dynamic code requirement is already satisfied.")]
265
#if NET7_0_OR_GREATER
266
    [RequiresDynamicCode("The native code for underlying implementations of serialize helper methods may not be available for a given type.")]
267
#endif // NET7_0_OR_GREATER
268
    private static Serialize<object> NullableSerializerFor([DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] Type nullableType, TomlSerializerOptions options)
269
#else
270
        private static Serialize<object> NullableSerializerFor(Type nullableType, TomlSerializerOptions options)
271
#endif
272
    {
1✔
273
        var serializer = _genericNullableSerializerMethod.MakeGenericMethod(nullableType.GetGenericArguments());
1✔
274
                    
275
        var del = Delegate.CreateDelegate(typeof(ComplexSerialize<>).MakeGenericType(nullableType), serializer);
1✔
276
        var ret = (Serialize<object>)(dict => (TomlValue?)del.DynamicInvoke(dict, options));
2✔
277
        Serializers[nullableType] = ret;
1✔
278
                    
279
        return ret;
1✔
280
    }
1✔
281

282
#if MODERN_DOTNET
283
#if NET7_0_OR_GREATER
284
    [RequiresDynamicCode("The native code for underlying implementations of deserialize helper methods may not be available for a given type.")]
285
#endif // NET7_0_OR_GREATER
286
    private static Deserialize<object> ArrayDeserializerFor([DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] Type elementType, TomlSerializerOptions options) =>
287
#else
288
        private static Deserialize<object> ArrayDeserializerFor(Type elementType, TomlSerializerOptions options) =>
289
#endif
290
        value =>
3✔
291
        {
13✔
292
            if (value is not TomlArray tomlArray)
13!
UNCOV
293
                throw new TomlTypeMismatchException(typeof(TomlArray), value.GetType(), elementType.MakeArrayType());
×
294

3✔
295
            var ret = Array.CreateInstance(elementType, tomlArray.Count);
13✔
296
            var deserializer = GetDeserializer(elementType, options);
13✔
297
            for (var index = 0; index < tomlArray.ArrayValues.Count; index++)
88✔
298
            {
31✔
299
                var arrayValue = tomlArray.ArrayValues[index];
31✔
300
                ret.SetValue(deserializer(arrayValue), index);
31✔
301
            }
31✔
302

3✔
303
            return ret;
13✔
304
        };
16✔
305

306
#if MODERN_DOTNET
307
    [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "The element type must have been used somewhere in the consuming code in order for this method to be called, so the dynamic code requirement is already satisfied.")]
308
    private static Deserialize<object> ListDeserializerFor([DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] Type elementType, TomlSerializerOptions options)
309
#else
310
        private static Deserialize<object> ListDeserializerFor(Type elementType, TomlSerializerOptions options)
311
#endif
312
    {
6✔
313
        var listType = typeof(List<>).MakeGenericType(elementType);
6✔
314
        var relevantAddMethod = listType.GetMethod("Add")!;
6✔
315

316
        return value =>
6✔
317
        {
10✔
318
            if (value is not TomlArray tomlArray)
10!
UNCOV
319
                throw new TomlTypeMismatchException(typeof(TomlArray), value.GetType(), listType);
×
320

6✔
321
            var ret = Activator.CreateInstance(listType)!;
10✔
322
            var deserializer = GetDeserializer(elementType, options);
10✔
323

6✔
324
            foreach (var arrayValue in tomlArray.ArrayValues)
78✔
325
            {
24✔
326
                relevantAddMethod.Invoke(ret, new[] { deserializer(arrayValue) });
24✔
327
            }
24✔
328

6✔
329
            return ret;
10✔
330
        };
16✔
331
    }
6✔
332
        
333
#if MODERN_DOTNET
334
    [UnconditionalSuppressMessage("AOT", "IL2062", Justification = "The nullableType's underlying T must have been used somewhere in the consuming code in order for this method to be called, so the dynamic code requirement is already satisfied.")]
335
#if NET7_0_OR_GREATER        
336
    [RequiresDynamicCode("The native code for underlying implementations of deserialize helper methods may not be available for a given type.")]
337
#endif // NET7_0_OR_GREATER
338
    private static Deserialize<object> NullableDeserializerFor([DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] Type nullableType, TomlSerializerOptions options)
339
#else 
340
        private static Deserialize<object> NullableDeserializerFor(Type nullableType, TomlSerializerOptions options)
341
#endif
342
    {
1✔
343
        var elementType = nullableType.GetGenericArguments()[0];
1✔
344
        var elementDeserializer = GetDeserializer(elementType, options);
1✔
345
            
346
        return value =>
1✔
347
        {
1✔
348
            //If we're deserializing, we know the value is not null
1✔
349
            var element = elementDeserializer(value);
1✔
350
            return Activator.CreateInstance(nullableType, element)!;
1✔
351
        };
2✔
352
    }
1✔
353

354
#if MODERN_DOTNET
355
#if NET7_0_OR_GREATER
356
    [RequiresDynamicCode("The native code for underlying implementations of deserialize helper methods may not be available for a given type.")]
357
#endif // NET7_0_OR_GREATER
358
    private static Deserialize<Dictionary<string, T>> StringKeyedDictionaryDeserializerFor<[DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] T>(TomlSerializerOptions options)
359
#else
360
        private static Deserialize<Dictionary<string, T>> StringKeyedDictionaryDeserializerFor<T>(TomlSerializerOptions options)
361
#endif
362
    {
1✔
363
        var deserializer = GetDeserializer(typeof(T), options);
1✔
364

365
        return value =>
1✔
366
        {
1✔
367
            if (value is not TomlTable table)
1!
UNCOV
368
                throw new TomlTypeMismatchException(typeof(TomlTable), value.GetType(), typeof(Dictionary<string, T>));
×
369

1✔
370
            return table.Entries.ToDictionary(entry => entry.Key, entry => (T)deserializer(entry.Value));
5✔
371
        };
2✔
372
    }
1✔
373

374
    // unmanaged + IConvertible is the closest I can get to expressing "primitives only"
375
#if MODERN_DOTNET
376
#if NET7_0_OR_GREATER
377
    [RequiresDynamicCode("The native code for underlying implementations of deserialize helper methods may not be available for a given type.")]
378
#endif // NET7_0_OR_GREATER
379
    private static Deserialize<Dictionary<TKey, TValue>> PrimitiveKeyedDictionaryDeserializerFor<TKey, [DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] TValue>(TomlSerializerOptions options) where TKey : unmanaged, IConvertible
380
#else
381
        private static Deserialize<Dictionary<TKey, TValue>> PrimitiveKeyedDictionaryDeserializerFor<TKey, TValue>(TomlSerializerOptions options) where TKey : unmanaged, IConvertible
382
#endif
383
    {
4✔
384
        var valueDeserializer = GetDeserializer(typeof(TValue), options);
4✔
385

386
        return value =>
4✔
387
        {
4✔
388
            if (value is not TomlTable table)
4!
UNCOV
389
                throw new TomlTypeMismatchException(typeof(TomlTable), value.GetType(), typeof(Dictionary<TKey, TValue>));
×
390

4✔
391
            return table.Entries.ToDictionary(
4✔
392
                entry => (TKey)(entry.Key as IConvertible).ToType(typeof(TKey), CultureInfo.InvariantCulture),
12✔
393
                entry => (TValue)valueDeserializer(entry.Value)
12✔
394
            );
4✔
395
        };
8✔
396
    }
4✔
397

398
#if MODERN_DOTNET
399
#if NET7_0_OR_GREATER
400
    [RequiresDynamicCode("The native code for underlying implementations of serialize helper methods may not be available for a given type.")]
401
#endif // NET7_0_OR_GREATER
402
    private static TomlValue? GenericNullableSerializer<[DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] T>(T? nullable, TomlSerializerOptions options) where T : struct
403
#else
404
        private static TomlValue? GenericNullableSerializer<T>(T? nullable, TomlSerializerOptions options) where T : struct
405
#endif
406
    {
1✔
407
        var elementSerializer = GetSerializer(typeof(T), options);
1✔
408
            
409
        if (nullable.HasValue)
1!
410
            return elementSerializer(nullable.Value);
1✔
411

UNCOV
412
        return null;
×
413
    }
1✔
414

415
#if MODERN_DOTNET
416
#if NET7_0_OR_GREATER
417
    [RequiresDynamicCode("The native code for underlying implementations of serialize helper methods may not be available for a given type.")]
418
#endif // NET7_0_OR_GREATER
419
    private static TomlValue GenericDictionarySerializer<TKey, [DynamicallyAccessedMembers(MainDeserializerAccessedMemberTypes)] TValue>(Dictionary<TKey, TValue> dict, TomlSerializerOptions options) where TKey : notnull
420
#else
421
        private static TomlValue GenericDictionarySerializer<TKey, TValue>(Dictionary<TKey, TValue> dict, TomlSerializerOptions options) where TKey : notnull
422
#endif
423
    {
5✔
424
        var valueSerializer = GetSerializer(typeof(TValue), options);
5✔
425

426
        var ret = new TomlTable();
5✔
427
        foreach (var entry in dict)
43✔
428
        {
14✔
429
            var keyAsString = entry.Key.ToString();
14✔
430
                
431
            if(keyAsString == null)
14✔
UNCOV
432
                continue;
×
433

434
            var value = valueSerializer(entry.Value);
14✔
435
                
436
            if(value == null)
14✔
UNCOV
437
                continue;
×
438
                
439
            ret.PutValue(keyAsString, value, true);
14✔
440
        }
14✔
441

442
        return ret;
5✔
443
    }
5✔
444

445
    internal static void Register<T>(Serialize<T>? serializer, Deserialize<T>? deserializer)
446
    {
15✔
447
        if (serializer != null)
15✔
448
        {
15✔
449
            RegisterSerializer(serializer);
15✔
450

451
            RegisterDictionarySerializer(serializer);
15✔
452
        }
15✔
453

454
        if (deserializer != null)
15✔
455
        {
15✔
456
            RegisterDeserializer(deserializer);
15✔
457
            RegisterDictionaryDeserializer(deserializer);
15✔
458
        }
15✔
459
    }
15✔
460

461
    internal static void Register(Type t, Serialize<object>? serializer, Deserialize<object>? deserializer)
462
    {
122✔
463
        if (serializer != null)
122✔
464
            RegisterSerializer(serializer);
70✔
465

466
        if (deserializer != null)
122✔
467
            RegisterDeserializer(deserializer);
52✔
468
    }
122✔
469

470
    private static void RegisterDeserializer<T>(Deserialize<T> deserializer)
471
    {
82✔
472
        object BoxedDeserializer(TomlValue value) => deserializer.Invoke(value) ?? throw new Exception($"TOML Deserializer returned null for type {nameof(T)}");
145!
473
        Deserializers[typeof(T)] = (Deserialize<object>)BoxedDeserializer;
82✔
474
    }
82✔
475

476
    private static void RegisterSerializer<T>(Serialize<T> serializer)
477
    {
100✔
478
        TomlValue? ObjectAcceptingSerializer(object value) => serializer.Invoke((T)value);
165✔
479
        Serializers[typeof(T)] = (Serialize<object>)ObjectAcceptingSerializer!;
100✔
480
    }
100✔
481

482
    private static void RegisterDictionarySerializer<T>(Serialize<T> serializer)
483
    {
15✔
484
        RegisterSerializer<Dictionary<string, T>>(dict =>
15✔
485
        {
6✔
486
            var table = new TomlTable();
6✔
487

15✔
488
            if (dict == null)
6!
UNCOV
489
                return table;
×
490

15✔
491
            var keys = dict.Keys.ToList();
6✔
492
            var values = dict.Values.Select(serializer.Invoke).ToList();
6✔
493

15✔
494
            for (var i = 0; i < keys.Count; i++)
36✔
495
            {
12✔
496
                var tomlValue = values[i];
12✔
497
                if (tomlValue == null)
12✔
498
                    //Skip null values
15✔
UNCOV
499
                    continue;
×
500
                    
15✔
501
                table.PutValue(keys[i], tomlValue, true);
12✔
502
            }
12✔
503

15✔
504
            return table;
6✔
505
        });
21✔
506
    }
15✔
507

508
    private static void RegisterDictionaryDeserializer<T>(Deserialize<T> deserializer)
509
    {
15✔
510
        RegisterDeserializer(value =>
15✔
511
        {
3✔
512
            if (value is not TomlTable table)
3!
UNCOV
513
                throw new TomlTypeMismatchException(typeof(TomlTable), value.GetType(), typeof(Dictionary<string, T>));
×
514

15✔
515
            return table.Entries
3✔
516
                .Select(kvp => new KeyValuePair<string, T>(kvp.Key, deserializer.Invoke(kvp.Value)))
6✔
517
                .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
15✔
518
        });
18✔
519
    }
15✔
520
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc