< Summary

Information
Class: Chronicler.ChronicleContext
Assembly: Chronicler
File(s): /home/runner/work/Chronicler/Chronicler/src/Chronicler/Context/ChronicleContext.cs
Line coverage
100%
Covered lines: 44
Uncovered lines: 0
Coverable lines: 44
Total lines: 126
Line coverage: 100%
Branch coverage
100%
Covered branches: 20
Total branches: 20
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
QueueDeferredLink(...)100%11100%
ResolveDeferredLinks()100%44100%
ResolveQueuedLinks()100%44100%
ResolveQueuedLinkPass()100%44100%
ThrowUnresolvedDeferredLinks()100%22100%
.ctor(...)100%22100%
TryResolve(...)100%22100%
Describe()100%22100%

File(s)

/home/runner/work/Chronicler/Chronicler/src/Chronicler/Context/ChronicleContext.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4
 5namespace Chronicler;
 6
 7/// <summary>
 8/// Carries session-scoped serialization services such as stable link resolution.
 9/// </summary>
 10public sealed class ChronicleContext
 11{
 16912    private readonly List<IDeferredRecordLink> _deferredLinks = new();
 13
 14    /// <summary>
 15    /// Creates a new context with an empty link registry.
 16    /// </summary>
 16917    public ChronicleContext()
 18    {
 16919        Links = new ChronicleLinkRegistry();
 16920    }
 21
 22    /// <summary>
 23    /// Gets the registry used to save and load stable links to external or runtime-owned objects.
 24    /// </summary>
 25    public ChronicleLinkRegistry Links { get; }
 26
 27    internal void QueueDeferredLink<T>(string name, string id, string? slot, Action<T> assignLoadedValue)
 28    {
 729        _deferredLinks.Add(new DeferredRecordLink<T>(name, id, slot, assignLoadedValue));
 630    }
 31
 32    /// <summary>
 33    /// Attempts to resolve all deferred links queued during loading.
 34    /// </summary>
 35    public void ResolveDeferredLinks()
 36    {
 4537        if (_deferredLinks.Count == 0)
 3938            return;
 39
 640        ResolveQueuedLinks();
 41
 642        if (_deferredLinks.Count > 0)
 443            ThrowUnresolvedDeferredLinks();
 244    }
 45
 46    private void ResolveQueuedLinks()
 47    {
 848        while (_deferredLinks.Count > 0)
 49        {
 650            if (ResolveQueuedLinkPass() == 0)
 451                return;
 52        }
 253    }
 54
 55    private int ResolveQueuedLinkPass()
 56    {
 657        int resolvedCount = 0;
 2458        for (int i = _deferredLinks.Count - 1; i >= 0; i--)
 59        {
 660            if (!_deferredLinks[i].TryResolve(Links))
 61                continue;
 62
 263            _deferredLinks.RemoveAt(i);
 264            resolvedCount++;
 65        }
 66
 667        return resolvedCount;
 68    }
 69
 70    private void ThrowUnresolvedDeferredLinks()
 71    {
 472        var builder = new StringBuilder();
 473        builder.Append("Unable to resolve deferred links:");
 1674        foreach (IDeferredRecordLink deferredLink in _deferredLinks)
 75        {
 476            builder.Append(' ');
 477            builder.Append('[');
 478            builder.Append(deferredLink.Describe());
 479            builder.Append(']');
 80        }
 81
 482        throw new InvalidOperationException(builder.ToString());
 83    }
 84
 85    private interface IDeferredRecordLink
 86    {
 87        bool TryResolve(ChronicleLinkRegistry registry);
 88
 89        string Describe();
 90    }
 91
 92    private sealed class DeferredRecordLink<T> : IDeferredRecordLink
 93    {
 94        private readonly string _name;
 95        private readonly string _id;
 96        private readonly string? _slot;
 97        private readonly Action<T> _assignLoadedValue;
 98
 799        public DeferredRecordLink(string name, string id, string? slot, Action<T> assignLoadedValue)
 100        {
 7101            _name = name;
 7102            _id = id;
 7103            _slot = slot;
 7104            _assignLoadedValue = assignLoadedValue ?? throw new ArgumentNullException(nameof(assignLoadedValue));
 6105        }
 106
 107        public bool TryResolve(ChronicleLinkRegistry registry)
 108        {
 109            T? value;
 6110            if (!registry.TryResolve(_id, out value, _slot))
 4111                return false;
 112
 2113            _assignLoadedValue(value!);
 2114            return true;
 115        }
 116
 117        public string Describe()
 118        {
 4119            string typeName = typeof(T).Name;
 4120            if (string.IsNullOrEmpty(_slot))
 2121                return $"{_name}:{typeName}:{_id}";
 122
 2123            return $"{_name}:{typeName}:{_id}@{_slot}";
 124        }
 125    }
 126}