File: Commands\PackageSearch\PackageSearchResultTableRenderer.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.CommandLine.XPlat\NuGet.CommandLine.XPlat.csproj (NuGet.CommandLine.XPlat)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#nullable enable

using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using NuGet.Configuration;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;

namespace NuGet.CommandLine.XPlat
{
    internal class PackageSearchResultTableRenderer : IPackageSearchResultRenderer
    {
        private string _searchTerm;
        private ILoggerWithColor _loggerWithColor;
        private const int LineSeparatorLength = 40;
        private static readonly string SourceSeparator = new('*', LineSeparatorLength);
        private PackageSearchVerbosity _verbosity;
        private bool _exactMatch;
        private int _consoleWidth;

        public PackageSearchResultTableRenderer(string searchTerm, ILoggerWithColor loggerWithColor, PackageSearchVerbosity verbosity, bool exactMatch, int consoleWidth)
        {
            _searchTerm = searchTerm;
            _loggerWithColor = loggerWithColor;
            _verbosity = verbosity;
            _exactMatch = exactMatch;
            _consoleWidth = consoleWidth;
        }

        public void Add(PackageSource source, IEnumerable<IPackageSearchMetadata> completedSearch)
        {
            _loggerWithColor.LogMinimal(SourceSeparator);
            _loggerWithColor.LogMinimal($"Source: {source.Name} ({source.SourceUri})");

            ITableFormatStrategy strategy = TableFormatStrategyFactory.GetStrategy(_verbosity, _exactMatch);
            Table table = strategy.CreateTable(_consoleWidth);
            PopulateTableWithResultsAsync(completedSearch, table, _verbosity);
            table.PrintResult(_searchTerm, _loggerWithColor);
        }

        public void Add(PackageSource source, PackageSearchProblem packageSearchProblem)
        {
            _loggerWithColor.LogMinimal(SourceSeparator);
            _loggerWithColor.LogMinimal($"Source: {source.Name} ({source.SourceUri})");

            if (packageSearchProblem.ProblemType == PackageSearchProblemType.Error)
            {
                _loggerWithColor.LogError(packageSearchProblem.Text);
            }
            else
            {
                _loggerWithColor.LogWarning(packageSearchProblem.Text);
            }
        }

        public void Finish()
        {
            // We don't need to write anything at the end of the rendering for a tabular format
        }

        public void Start()
        {
            // We don't need to write anything at the beginning of the rendering for a tabular format
        }

        /// <summary>
        /// Populates the given table with package metadata results.
        /// </summary>
        /// <param name="results">An enumerable of package search metadata to be processed and added to the table.</param>
        /// <param name="table">The table where the results will be added as rows.</param>
        /// <param name="verbosity">The verbosity level for the search results.</param>
        private static async void PopulateTableWithResultsAsync(IEnumerable<IPackageSearchMetadata> results, Table table, PackageSearchVerbosity verbosity)
        {
            CultureInfo culture = CultureInfo.CurrentCulture;
            NumberFormatInfo nfi = (NumberFormatInfo)culture.NumberFormat.Clone();
            nfi.NumberDecimalDigits = 0;

            foreach (IPackageSearchMetadata result in results)
            {
                string packageId = result.Identity.Id;
                string version = result.Identity.Version.ToNormalizedString();
                string owners = result.Owners;
                string downloads = "N/A";

                if (result.DownloadCount != null)
                {
                    downloads = string.Format(nfi, "{0:N}", result.DownloadCount);
                }

                if (verbosity == PackageSearchVerbosity.Minimal)
                {
                    table.AddRow(packageId, version);
                }
                else if (verbosity == PackageSearchVerbosity.Detailed)
                {
                    PackageDeprecationMetadata packageDeprecationMetadata = await result.GetDeprecationMetadataAsync();
                    string vulnerable = "N/A";
                    string projectUri = "N/A";
                    string deprecation = "N/A";

                    if (result.Vulnerabilities?.Any() ?? false)
                    {
                        vulnerable = "True";
                    }

                    if (result.ProjectUrl is not null)
                    {
                        projectUri = result.ProjectUrl.ToString();
                    }

                    if (packageDeprecationMetadata is not null)
                    {
                        deprecation = packageDeprecationMetadata.Message ?? "N/A";
                    }

                    table.AddRow(
                        packageId,
                        version,
                        owners,
                        downloads,
                        vulnerable,
                        deprecation,
                        projectUri,
                        result.Description);
                }
                else
                {
                    table.AddRow(packageId, version, owners, downloads);
                }
            }
        }

        public void Add(PackageSearchProblem packageSearchProblem)
        {
            if (packageSearchProblem.ProblemType == PackageSearchProblemType.Error)
            {
                _loggerWithColor.LogError(packageSearchProblem.Text);
            }
            else
            {
                _loggerWithColor.LogWarning(packageSearchProblem.Text);
            }
        }
    }
}