using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using StellaOps.DependencyInjection; using StellaOps.Plugin.Internal; using System; using System.Linq; using System.Reflection; namespace StellaOps.Plugin.DependencyInjection; public static class PluginServiceRegistration { public static void RegisterAssemblyMetadata(IServiceCollection services, Assembly assembly, ILogger? logger) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(assembly); foreach (var implementationType in assembly.GetLoadableTypes()) { if (implementationType is null || !implementationType.IsClass || implementationType.IsAbstract) { continue; } var attributes = implementationType.GetCustomAttributes(inherit: false); if (!attributes.Any()) { continue; } foreach (var attribute in attributes) { try { ApplyBinding(services, implementationType, attribute, logger); } catch (Exception ex) { logger?.LogWarning( ex, "Failed to register service binding for implementation '{Implementation}' declared in assembly '{Assembly}'.", implementationType.FullName ?? implementationType.Name, assembly.FullName ?? assembly.GetName().Name); } } } } private static void ApplyBinding( IServiceCollection services, Type implementationType, ServiceBindingAttribute attribute, ILogger? logger) { var serviceType = attribute.ServiceType ?? implementationType; if (!IsValidBinding(serviceType, implementationType)) { logger?.LogWarning( "Service binding metadata ignored: implementation '{Implementation}' is not assignable to service '{Service}'.", implementationType.FullName ?? implementationType.Name, serviceType.FullName ?? serviceType.Name); return; } if (attribute.ReplaceExisting) { RemoveExistingDescriptors(services, serviceType); } AddDescriptorIfMissing(services, serviceType, implementationType, attribute.Lifetime, logger); if (attribute.RegisterAsSelf && serviceType != implementationType) { AddDescriptorIfMissing(services, implementationType, implementationType, attribute.Lifetime, logger); } } private static bool IsValidBinding(Type serviceType, Type implementationType) { if (serviceType.IsGenericTypeDefinition) { return implementationType.IsGenericTypeDefinition && implementationType.IsClass && implementationType.IsAssignableToGenericTypeDefinition(serviceType); } return serviceType.IsAssignableFrom(implementationType); } private static void AddDescriptorIfMissing( IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime lifetime, ILogger? logger) { if (services.Any(descriptor => descriptor.ServiceType == serviceType && descriptor.ImplementationType == implementationType)) { logger?.LogDebug( "Skipping duplicate service binding for {ServiceType} -> {ImplementationType}.", serviceType.FullName ?? serviceType.Name, implementationType.FullName ?? implementationType.Name); return; } ServiceDescriptor descriptor; if (serviceType.IsGenericTypeDefinition || implementationType.IsGenericTypeDefinition) { descriptor = ServiceDescriptor.Describe(serviceType, implementationType, lifetime); } else { descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime); } services.Add(descriptor); logger?.LogDebug( "Registered service binding {ServiceType} -> {ImplementationType} with {Lifetime} lifetime.", serviceType.FullName ?? serviceType.Name, implementationType.FullName ?? implementationType.Name, lifetime); } private static void RemoveExistingDescriptors(IServiceCollection services, Type serviceType) { for (var i = services.Count - 1; i >= 0; i--) { if (services[i].ServiceType == serviceType) { services.RemoveAt(i); } } } private static bool IsAssignableToGenericTypeDefinition(this Type implementationType, Type serviceTypeDefinition) { if (!serviceTypeDefinition.IsGenericTypeDefinition) { return false; } if (implementationType == serviceTypeDefinition) { return true; } if (implementationType.IsGenericType && implementationType.GetGenericTypeDefinition() == serviceTypeDefinition) { return true; } var interfaces = implementationType.GetInterfaces(); foreach (var iface in interfaces) { if (iface.IsGenericType && iface.GetGenericTypeDefinition() == serviceTypeDefinition) { return true; } } var baseType = implementationType.BaseType; return baseType is not null && baseType.IsGenericTypeDefinition ? baseType.GetGenericTypeDefinition() == serviceTypeDefinition : baseType is not null && baseType.IsAssignableToGenericTypeDefinition(serviceTypeDefinition); } }