File: src\roslyn\src\Workspaces\SharedUtilitiesAndExtensions\Workspace\Core\Workspace\Mef\LayeredServiceUtilities.cs
Web Access
Project: Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Collections;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Host.Mef;

internal static class LayeredServiceUtilities
{
    /// <summary>
    /// Layers in the priority order. <see cref="ServiceLayer.Host"/> services override <see cref="ServiceLayer.Editor"/> services, etc.
    /// </summary>
    private static readonly ImmutableArray<string> s_orderedProductLayers = [ServiceLayer.Host, ServiceLayer.Editor, ServiceLayer.Desktop, ServiceLayer.Default];

    public static string GetAssemblyQualifiedServiceTypeName(Type type, string argName)
        => (type ?? throw new ArgumentNullException(argName)).AssemblyQualifiedName ?? throw new ArgumentException("Invalid service type", argName);

    public static (Lazy<TServiceInterface, TMetadata>? lazyService, bool usesFactory) PickService<TServiceInterface, TMetadata>(
        Type serviceType,
        string? workspaceKind,
        IEnumerable<(Lazy<TServiceInterface, TMetadata>? lazyService, bool usesFactory)> services)
        where TMetadata : ILayeredServiceMetadata
    {
        (Lazy<TServiceInterface, TMetadata>? lazyService, bool usesFactory) service;
        TemporaryArray<(Lazy<TServiceInterface, TMetadata>? lazyService, bool usesFactory)> servicesOfMatchingType = new();

        // PERF: Hoist AssemblyQualifiedName out of the loop to avoid repeated string allocations.
        var assemblyQualifiedName = serviceType.AssemblyQualifiedName;

        foreach (var entry in services)
        {
            if (entry.lazyService?.Metadata.ServiceType == assemblyQualifiedName)
            {
                servicesOfMatchingType.Add(entry);
            }
        }

#if WORKSPACE
        // test layer overrides all other layers and workspace kinds:
        service = servicesOfMatchingType.SingleOrDefault(static lz => lz.lazyService?.Metadata.Layer == ServiceLayer.Test);
        if (service.lazyService != null)
        {
            return service;
        }
#endif
        // If a service is exported for specific workspace kinds and the current workspace kind is among them, use it.
        if (workspaceKind != null)
        {
            service = servicesOfMatchingType.SingleOrDefault(static (lz, workspaceKind) => lz.lazyService?.Metadata.WorkspaceKinds.Contains(workspaceKind) ?? false, workspaceKind);
            if (service.lazyService != null)
            {
                return service;
            }

            // For backward compat check workspace kind specific service.
            // Workspace kind specific service should specify supported kinds in WorkspaceKinds.
            service = TryGetServiceByLayer(workspaceKind);
            if (service.lazyService != null)
            {
                return service;
            }
        }

        foreach (var layer in s_orderedProductLayers)
        {
            service = TryGetServiceByLayer(layer);
            if (service.lazyService != null)
            {
                return service;
            }
        }

        // no service.
        return default;

        (Lazy<TServiceInterface, TMetadata>? lazyService, bool usesFactory) TryGetServiceByLayer(string layer)
            => servicesOfMatchingType.SingleOrDefault(static (lz, layer) => lz.lazyService?.Metadata.WorkspaceKinds is [] && lz.lazyService.Metadata.Layer == layer, layer);
    }
}