onebeyond/onebeyond-studio-core

View on GitHub
src/OneBeyond.Studio.Application.SharedKernel/AmbientContexts/AmbientContextAccessorOverrider.cs

Summary

Maintainability
A
0 mins
Test Coverage
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using EnsureThat;
using OneBeyond.Studio.Crosscuts.Utilities.LogicalCallContext;

namespace OneBeyond.Studio.Application.SharedKernel.AmbientContexts;

public sealed class AmbientContextAccessorOverrider<TAmbientContext> : IAmbientContextAccessor<TAmbientContext>
    where TAmbientContext : AmbientContext
{
    public static IDisposable OverrideWith(IAmbientContextAccessor<TAmbientContext> ambientContextAccessor)
        => new AmbientContextAccessorOverride(ambientContextAccessor);

    internal AmbientContextAccessorOverrider(IAmbientContextAccessor<TAmbientContext> ambientContextAccessor)
    {
        EnsureArg.IsNotNull(ambientContextAccessor, nameof(ambientContextAccessor));

        _ambientContextAccessor = ambientContextAccessor;
    }

    internal AmbientContextAccessorOverrider(IAmbientContextAccessor ambientContextAccessor)
    {
        EnsureArg.IsAssignableToType(
            ambientContextAccessor,
            typeof(IAmbientContextAccessor<TAmbientContext>),
            nameof(ambientContextAccessor));

        _ambientContextAccessor = (IAmbientContextAccessor<TAmbientContext>)ambientContextAccessor;
    }

    private const string AmbientContextAccessorStackName = "AmbientContextAccessorStack";
    private readonly IAmbientContextAccessor<TAmbientContext> _ambientContextAccessor;

    TAmbientContext IAmbientContextAccessor<TAmbientContext>.AmbientContext => TryGetOverride(out var ambientContextAccessor)
        ? ambientContextAccessor.AmbientContext
        : _ambientContextAccessor.AmbientContext;

    AmbientContext IAmbientContextAccessor.AmbientContext => TryGetOverride(out var ambientContextAccessor)
        ? ambientContextAccessor.AmbientContext
        : _ambientContextAccessor.AmbientContext;

    private static bool TryGetOverride([NotNullWhen(true)] out IAmbientContextAccessor<TAmbientContext>? ambientContextAccessor)
    {
        var ambientContextAccessorStack =
            LogicalCallContext.FindData<Stack<IAmbientContextAccessor<TAmbientContext>>>(AmbientContextAccessorStackName);
        if (ambientContextAccessorStack?.Count > 0)
        {
            ambientContextAccessor = ambientContextAccessorStack.Peek();
            return true;
        }
        ambientContextAccessor = null;
        return false;
    }

    private sealed class AmbientContextAccessorOverride : IDisposable
    {
        public AmbientContextAccessorOverride(IAmbientContextAccessor<TAmbientContext> ambientContextAccessor)
        {
            EnsureArg.IsNotNull(ambientContextAccessor, nameof(ambientContextAccessor));

            var ambientContextAccessorStack =
                LogicalCallContext.FindData<Stack<IAmbientContextAccessor<TAmbientContext>>>(AmbientContextAccessorStackName);
            if (ambientContextAccessorStack is null)
            {
                ambientContextAccessorStack = new Stack<IAmbientContextAccessor<TAmbientContext>>();
                LogicalCallContext.SetData(AmbientContextAccessorStackName, ambientContextAccessorStack);
            }
            ambientContextAccessorStack.Push(ambientContextAccessor);
            _ambientContextAccessor = ambientContextAccessor;
        }

        private IAmbientContextAccessor<TAmbientContext>? _ambientContextAccessor;

        public void Dispose()
        {
            var ambientContextAccessorStack =
                LogicalCallContext.FindData<Stack<IAmbientContextAccessor<TAmbientContext>>>(AmbientContextAccessorStackName)
                ?? throw new InvalidOperationException("Unable to retrieve disposed ambient context accessor override.");
            if (!ReferenceEquals(ambientContextAccessorStack.Peek(), _ambientContextAccessor))
            {
                throw new InvalidOperationException("Ambient context accessor overriding order is broken.");
            }
            ambientContextAccessorStack.Pop();
            _ambientContextAccessor = null;
        }
    }
}