< Summary

Information
Class: Chronicler.StateJsonConverterFactory
Assembly: Chronicler
File(s): /home/runner/work/Chronicler/Chronicler/src/Chronicler/Serialization/Json/StateJsonConverterFactory.cs
Line coverage
93%
Covered lines: 31
Uncovered lines: 2
Coverable lines: 33
Total lines: 88
Line coverage: 93.9%
Branch coverage
87%
Covered branches: 21
Total branches: 24
Branch coverage: 87.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
CanConvert(...)100%66100%
CreateConverter(...)75%44100%
HasStateBackedContract(...)100%44100%
GetSingleStateType(...)75%8881.81%
IsStateBackedInterface(...)100%22100%
CreateFactory(...)100%11100%

File(s)

/home/runner/work/Chronicler/Chronicler/src/Chronicler/Serialization/Json/StateJsonConverterFactory.cs

#LineLine coverage
 1using System;
 2using System.Linq.Expressions;
 3using System.Reflection;
 4using System.Text.Json;
 5using System.Text.Json.Serialization;
 6
 7namespace Chronicler;
 8
 9/// <summary>
 10/// Creates JSON converters for types that explicitly implement <see cref="IStateBacked{TState}"/>.
 11/// </summary>
 12public sealed class StateJsonConverterFactory : JsonConverterFactory
 13{
 14    /// <inheritdoc />
 15    public override bool CanConvert(Type typeToConvert)
 16    {
 517        return typeToConvert != null
 518            && typeToConvert.IsClass
 519            && !typeToConvert.IsAbstract
 520            && HasStateBackedContract(typeToConvert);
 21    }
 22
 23    /// <inheritdoc />
 24    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
 25    {
 226        Type stateType = GetSingleStateType(typeToConvert);
 27
 228        ConstructorInfo constructor = typeToConvert.GetConstructor(new[] { stateType })
 229            ?? throw new InvalidOperationException(
 230                $"Type '{typeToConvert}' must define a public constructor accepting '{stateType}'.");
 31
 132        Type converterType = typeof(StateJsonConverter<,>).MakeGenericType(typeToConvert, stateType);
 133        Delegate factory = CreateFactory(typeToConvert, stateType, constructor);
 34
 135        return (JsonConverter)(Activator.CreateInstance(converterType, factory)
 136            ?? throw new InvalidOperationException($"Failed to create converter instance for type '{converterType}'."));
 37    }
 38
 39    private static bool HasStateBackedContract(Type type)
 40    {
 2741        foreach (Type interfaceType in type.GetInterfaces())
 42        {
 1043            if (IsStateBackedInterface(interfaceType))
 144                return true;
 45        }
 46
 347        return false;
 48    }
 49
 50    private static Type GetSingleStateType(Type type)
 51    {
 252        Type? stateType = null;
 53
 854        foreach (Type interfaceType in type.GetInterfaces())
 55        {
 256            if (!IsStateBackedInterface(interfaceType))
 57                continue;
 58
 259            Type currentStateType = interfaceType.GetGenericArguments()[0];
 260            if (stateType != null)
 61            {
 062                throw new InvalidOperationException(
 063                    $"Type '{type}' must implement only one IStateBacked<TState> contract.");
 64            }
 65
 266            stateType = currentStateType;
 67        }
 68
 269        return stateType
 270            ?? throw new InvalidOperationException(
 271                $"Type '{type}' must implement IStateBacked<TState>.");
 72    }
 73
 74    private static bool IsStateBackedInterface(Type type)
 75    {
 1276        return type.IsGenericType
 1277            && type.GetGenericTypeDefinition() == typeof(IStateBacked<>);
 78    }
 79
 80    private static Delegate CreateFactory(Type recordType, Type stateType, ConstructorInfo constructor)
 81    {
 182        ParameterExpression stateParameter = Expression.Parameter(stateType, "state");
 183        NewExpression createRecord = Expression.New(constructor, stateParameter);
 184        Type factoryType = typeof(Func<,>).MakeGenericType(stateType, recordType);
 85
 186        return Expression.Lambda(factoryType, createRecord, stateParameter).Compile();
 87    }
 88}