Skip to content

Commit

Permalink
Add dotnet package search (#5466)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nigusu-Allehu committed Nov 22, 2023
1 parent 229dddd commit 0e91cb7
Show file tree
Hide file tree
Showing 17 changed files with 1,617 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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.

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

namespace NuGet.CommandLine.XPlat
{
/// <summary>
/// Defines methods for rendering the search results of NuGet packages.
/// </summary>
internal interface IPackageSearchResultRenderer
{
/// <summary>
/// Starts the rendering operation.
/// </summary>
void Start();

/// <summary>
/// Adds a list of packages from a source to be rendered.
/// </summary>
void Add(PackageSource source, IEnumerable<IPackageSearchMetadata> completedSearch);

/// <summary>
/// Adds a message for a source, if search is unsuccessful.
/// </summary>
/// <param name="source">The source</param>
/// <param name="error">The error message to be rendered</param>
/// <returns></returns>
void Add(PackageSource source, string error);

/// <summary>
/// Finishes the rendering operation.
/// </summary>
void Finish();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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.

using System;
using System.Collections.Generic;
using System.Globalization;

namespace NuGet.CommandLine.XPlat
{
internal class PackageSearchArgs
{
private const int DefaultSkip = 0;
private const int DefaultTake = 20;
public List<string> Sources { get; set; }
public int Skip { get; set; }
public int Take { get; set; }
public bool Prerelease { get; set; }
public bool ExactMatch { get; set; }
public bool Interactive { get; set; }
public ILoggerWithColor Logger { get; set; }
public string SearchTerm { get; set; }

public PackageSearchArgs(string skip, string take)
{
Skip = VerifyInt(skip, DefaultSkip);
Take = VerifyInt(take, DefaultTake);
}

public PackageSearchArgs() { }

public int VerifyInt(string number, int defaultValue)
{
if (string.IsNullOrEmpty(number))
{
return defaultValue;
}

if (int.TryParse(number, out int verifiedNumber))
{
return verifiedNumber;
}

throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Error_invalid_number, number));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// 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.

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.CommandLineUtils;
using NuGet.Configuration;
using NuGet.Credentials;

namespace NuGet.CommandLine.XPlat
{
internal class PackageSearchCommand
{
public static void Register(CommandLineApplication app, Func<ILoggerWithColor> getLogger)
{
Register(app, getLogger, SetupSettingsAndRunSearchAsync);
}

public static void Register(CommandLineApplication app, Func<ILoggerWithColor> getLogger, Func<PackageSearchArgs, Task<int>> setupSettingsAndRunSearchAsync)
{
app.Command("search", pkgSearch =>
{
pkgSearch.Description = Strings.pkgSearch_Description;
CommandOption help = pkgSearch.HelpOption(XPlatUtility.HelpOption);
CommandArgument searchTerm = pkgSearch.Argument(
"<Search Term>",
Strings.pkgSearch_termDescription);
CommandOption sources = pkgSearch.Option(
"--source",
Strings.pkgSearch_SourceDescription,
CommandOptionType.MultipleValue);
CommandOption exactMatch = pkgSearch.Option(
"--exact-match",
Strings.pkgSearch_ExactMatchDescription,
CommandOptionType.NoValue);
CommandOption prerelease = pkgSearch.Option(
"--prerelease",
Strings.pkgSearch_PrereleaseDescription,
CommandOptionType.NoValue);
CommandOption interactive = pkgSearch.Option(
"--interactive",
Strings.pkgSearch_InteractiveDescription,
CommandOptionType.NoValue);
CommandOption take = pkgSearch.Option(
"--take",
Strings.pkgSearch_TakeDescription,
CommandOptionType.SingleValue);
CommandOption skip = pkgSearch.Option(
"--skip",
Strings.pkgSearch_SkipDescription,
CommandOptionType.SingleValue);

pkgSearch.OnExecute(async () =>
{
PackageSearchArgs packageSearchArgs;
ILoggerWithColor logger = getLogger();
try
{
packageSearchArgs = new PackageSearchArgs(skip.Value(), take.Value())
{
Sources = sources.Values,
SearchTerm = searchTerm.Value,
ExactMatch = exactMatch.HasValue(),
Interactive = interactive.HasValue(),
Prerelease = prerelease.HasValue(),
Logger = logger,
};
}
catch (ArgumentException ex)
{
logger.LogError(ex.Message);
return 1;
}

return await setupSettingsAndRunSearchAsync(packageSearchArgs);
});
});
}

public static async Task<int> SetupSettingsAndRunSearchAsync(PackageSearchArgs packageSearchArgs)
{
DefaultCredentialServiceUtility.SetupDefaultCredentialService(packageSearchArgs.Logger, !packageSearchArgs.Interactive);

ISettings settings = Settings.LoadDefaultSettings(
Directory.GetCurrentDirectory(),
configFileName: null,
machineWideSettings: new XPlatMachineWideSetting());
PackageSourceProvider sourceProvider = new PackageSourceProvider(settings);

// If a search lasts more than 15 minutes it is canceled.
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(15));

return await PackageSearchRunner.RunAsync(
sourceProvider,
packageSearchArgs,
cts.Token);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// 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.

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

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

public PackageSearchResultTablePrinter(string searchTerm, ILoggerWithColor loggerWithColor)
{
_searchTerm = searchTerm;
_loggerWithColor = loggerWithColor;
}

public void Add(PackageSource source, IEnumerable<IPackageSearchMetadata> completedSearch)
{
_loggerWithColor.LogMinimal(SourceSeparator);
_loggerWithColor.LogMinimal($"Source: {source.Name} ({source.SourceUri})");
var table = new Table(new[] { 0, 2 }, "Package ID", "Latest Version", "Authors", "Downloads");
PopulateTableWithResults(completedSearch, table);
table.PrintResult(_searchTerm, _loggerWithColor);
}

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

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>
private static void PopulateTableWithResults(IEnumerable<IPackageSearchMetadata> results, Table table)
{
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 authors = result.Authors;
string downloads = "N/A";

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

table.AddRow(packageId, version, authors, downloads);
}
}
}
}
Loading

0 comments on commit 0e91cb7

Please sign in to comment.