Cmdlets/src/XpandPwsh.Cmdlets/Nuget/Get-NugetPackage.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Frameworks;
using NuGet.Protocol.Core.Types;
using XpandPwsh.CmdLets;
 
namespace XpandPwsh.Cmdlets.Nuget{
    [Cmdlet(VerbsCommon.Get, "NugetPackage")]
    [CmdletBinding]
    [CmdLetTag(CmdLetTag.Nuget,CmdLetTag.Reactive,CmdLetTag.RX)][PublicAPI]
    public class GetNugetPackage : NugetCmdlet{
        [Parameter(ValueFromPipeline = true,Position = 0)]
        public string Name{ get; set; }
        [Parameter( Position = 2)]
        public string[] Source{ get; set; }
 
        [Parameter(Position = 1)]
        public string OutputFolder{ get; set; } = $@"{Path.GetTempPath()}\{nameof(GetNugetPackage)}";
        [Parameter]
        public SwitchParameter AllVersions{ get; set; }
        [Parameter]
        public string[] Versions{ get; set; }
        [Parameter]
        public SwitchParameter AllFiles{ get; set; }
        [Parameter]
        public NugetPackageResultType ResultType{ get; set; }
        protected override Task BeginProcessingAsync(){
            if (OutputFolder == null){
                OutputFolder = Path.Combine(Path.GetTempPath(), Name);
            }
 
            if (Source == null){
                Source = new[]{"https://api.nuget.org/v3/index.json"};
            }
            return base.BeginProcessingAsync();
        }
 
        protected override Task ProcessRecordAsync(){
            return GetDownloadResults()
                .Select(result => {
                    switch (ResultType){
                        case NugetPackageResultType.Default:
                            return NugetPackageAssemblies(result).ToObservable().OfType<object>();
                        case NugetPackageResultType.DownloadResults:
                            return Observable.Return(result).OfType<object>();
                        case NugetPackageResultType.NuSpecFile:
                            return Observable.Return(new FileInfo(result.PackageReader.GetNuspecFile()));
                        case NugetPackageResultType.NupkgFile:{
                            var directoryName = $"{Path.GetDirectoryName(result.PackageReader.GetNuspecFile())}";
                            return Observable.Return(Directory.GetFiles(directoryName,"*.nupkg",SearchOption.TopDirectoryOnly).First());
                        }
                    }
 
                    return Observable.Throw<object>(new NotImplementedException(ResultType.ToString()));
                })
                .Concat()
                .DefaultIfEmpty()
                .HandleErrors(this,Name??OutputFolder)
                .WriteObject(this)
                .ToTask();
        }
 
        private IEnumerable<NugetPackageAssembly> NugetPackageAssemblies(DownloadResourceResult result){
            return result.PackageReader.GetLibItems().SelectMany(group => group.Items
                    .Where(s => AllFiles || Path.GetExtension(s) == ".dll")
                    .Select(s => {
                        var nuspecReader = result.PackageReader.NuspecReader;
                        var identity = nuspecReader.GetIdentity();
                        return new NugetPackageAssembly{
                                Package = identity.Id,
                                Version = identity.Version.Version.ToString(),
                                DotNetFramework = group.TargetFramework.GetDotNetFrameworkName(DefaultFrameworkNameProvider.Instance),
                                File = $@"{s.Replace(@"/", @"\")}"
                            };
                    }));
        }
 
        private IObservable<DownloadResourceResult> GetDownloadResults(){
            var sourceSearchMetadatas = PackageSourceSearchMetadatas(Source.First()).Take(1);
            var packageSourceSearchMetadatas = sourceSearchMetadatas.ToObservable().Replay().RefCount();
            var downloadContext = new PackageDownloadContext(new SourceCacheContext());
            var providers = new List<Lazy<INuGetResourceProvider>>();
            providers.AddRange(Repository.Provider.GetCoreV3());
 
            var downloads = Source.ToObservable().Select(s => {
                    var repository = new SourceRepository(new PackageSource(s), providers);
                    return repository.GetResourceAsync<DownloadResource>().ToObservable()
                        .Select(resource => (resource, repository));
                }).Concat()
                .Replay().RefCount();
            return packageSourceSearchMetadatas
                .Select(metadata => downloads
                    .Select(tuple => tuple.resource.GetDownloadResourceResultAsync(metadata.Identity,
                        downloadContext, OutputFolder, NullLogger.Instance, CancellationToken.None)))
                .Concat().Concat()
                .Where(result => result.Status == DownloadResourceResultStatus.Available)
                .HandleErrors(this, Name); }
 
        private Collection<IPackageSearchMetadata> PackageSourceSearchMetadatas(string source,int index=0){
             
            string allVersions = $"-{nameof(AllVersions)} {AllVersions}";
            if (!AllVersions){
                allVersions = null;
            }
            string sources = $"-{nameof(Source)} '{Source[index]}'";
            index++;
            string versions = null;
            if (Versions != null){
                versions = $"-{nameof(Versions)} @({string.Join(",", Versions.Select(s => $"'{s}'"))})";
            }
 
            string name=$"-{nameof(Name)} {Name}";
            if (Name == null){
                name = null;
            }
 
            var cmdletName = CmdletExtensions.GetCmdletName<GetNugetPackageSearchMetadata>();
            var script = $"{cmdletName} {sources} {allVersions} {name} {versions} -{nameof(GetNugetPackageSearchMetadata.IncludeDelisted)}";
            var sourceSearchMetadatas = this.Invoke<IPackageSearchMetadata>(script);
            if (sourceSearchMetadatas == null!||!sourceSearchMetadatas.Any()){
                if (index==Source.Length){
                    throw new ItemNotFoundException($"{Name}---{script}");
                }
 
                return PackageSourceSearchMetadatas(source, index);
            }
            return sourceSearchMetadatas;
        }
    }
 
    public enum NugetPackageResultType{
        Default,
        DownloadResults,
        NuSpecFile,
        NupkgFile
    }
 
    public class NugetPackageAssembly:INugetPackageAssembly{
        public string Package{ get; set; }
        public string Version{ get; set; }
        public string DotNetFramework{ get; set; }
        public string File{ get; set; }
    }
    public interface INugetPackageAssembly{
        string Package{ get; set; }
        string Version{ get; set; }
        string DotNetFramework{ get; set; }
        string File{ get; set; }
    }
}