Cmdlets/src/XpandPwsh.Cmdlets/Get-ReferenceConflict/Get-ReferenceConflict.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Reflection;
using JetBrains.Annotations;
 
namespace XpandPwsh.Cmdlets{
    [Cmdlet(VerbsCommon.Get, "ReferenceConflict")]
    [CmdletBinding]
    [CmdLetTag(CmdLetTag.DotNet)][PublicAPI]
    public class GetReferenceConflict : PSCmdlet{
        [Parameter]
        public string Path{ get; set; } = Environment.CurrentDirectory;
 
        [Parameter]
        public string Filter{ get; set; } = "*";
        [Parameter]
        public SwitchParameter Recurse{ get; set; }
        protected override void ProcessRecord(){
            base.ProcessRecord();
            var multipleVersions = GetReferencedAssembliesWithMultipleVersions(Path).ToArray();
            var pairs = multipleVersions.Select(tuples => new{
                tuples.Key, Conflicts = tuples.Select(tuple => new{Assembly = tuple.assembly, Reference = tuple.referenceASsembly})
            });
            var wildcardPattern = new WildcardPattern(Filter,WildcardOptions.Compiled|WildcardOptions.IgnoreCase);
            var results = pairs.Where(__ =>wildcardPattern.IsMatch(__.Key)).ToArray();
            if (results.Length == 1){
                WriteObject(results.First().Conflicts);
            }
            else{
                foreach (var pair in results){
                    WriteObject(pair);
                }
            }
        }
 
        public IEnumerable<IGrouping<string, (AssemblyName assembly, AssemblyName referenceASsembly)>> GetReferencedAssembliesWithMultipleVersions(string path){
            var assemblies = GetAllAssemblies(path);
            var references = GetReferencesFromAllAssemblies(assemblies);
 
            return references
                .GroupBy(r => r.referenceASsembly.Name)
                .Where(r => r.Select(t => t.referenceASsembly.FullName).Distinct().Count() > 1);
        }
 
        private IEnumerable<(AssemblyName assembly, AssemblyName referenceASsembly)> GetReferencesFromAllAssemblies(IEnumerable<Assembly> assemblies){
            return assemblies.SelectMany(GetReferencedAssemblies);
        }
 
        private IEnumerable<(AssemblyName assembly, AssemblyName referenceASsembly)> GetReferencedAssemblies(Assembly asm){
            return asm.GetReferencedAssemblies().Select(refAsm => (asm.GetName(), refAsm));
        }
 
        private IEnumerable<Assembly> GetAllAssemblies(string path){
            return GetFileNames(path, "*.dll", "*.exe")
                .Select(TryLoadAssembly)
                .Where(asm => asm != null);
        }
 
        private IEnumerable<string> GetFileNames(string path, params string[] extensions){
            return extensions.SelectMany(ext => Directory.GetFiles(path, ext, Recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly));
        }
 
        private Assembly TryLoadAssembly(string filename){
            try{
                return Assembly.LoadFile(filename);
            }
            catch (BadImageFormatException){
                return null;
            }
        }
    }
}