Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global install filters #3458

Merged
merged 1 commit into from
Apr 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
289 changes: 289 additions & 0 deletions Cmdline/Action/Filter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
using System.Linq;
using System.Collections.Generic;

using Autofac;
using CommandLine;
using CommandLine.Text;
using log4net;

namespace CKAN.CmdLine
{
/// <summary>
/// Subcommand for managing installation filters
/// </summary>
public class Filter : ISubCommand
{
/// <summary>
/// Initialize the subcommand
/// </summary>
public Filter() { }

/// <summary>
/// Run the subcommand
/// </summary>
/// <param name="mgr">Manager to provide game instances</param>
/// <param name="opts">Command line parameters paritally handled by parser</param>
/// <param name="unparsed">Command line parameters not yet handled by parser</param>
/// <returns>
/// Exit code
/// </returns>
public int RunSubCommand(GameInstanceManager mgr, CommonOptions opts, SubCommandOptions unparsed)
{
string[] args = unparsed.options.ToArray();
int exitCode = Exit.OK;
// Parse and process our sub-verbs
Parser.Default.ParseArgumentsStrict(args, new FilterSubOptions(), (string option, object suboptions) =>
{
// ParseArgumentsStrict calls us unconditionally, even with bad arguments
if (!string.IsNullOrEmpty(option) && suboptions != null)
{
CommonOptions options = (CommonOptions)suboptions;
options.Merge(opts);
user = new ConsoleUser(options.Headless);
manager = mgr ?? new GameInstanceManager(user);
exitCode = options.Handle(manager, user);
if (exitCode != Exit.OK)
return;

switch (option)
{
case "list":
exitCode = ListFilters((FilterListOptions)suboptions, option);
break;

case "add":
exitCode = AddFilters((FilterAddOptions)suboptions, option);
break;

case "remove":
exitCode = RemoveFilters((FilterRemoveOptions)suboptions, option);
break;

default:
user.RaiseMessage("Unknown command: filter {0}", option);
exitCode = Exit.BADOPT;
break;
}
}
}, () => { exitCode = MainClass.AfterHelp(); });
return exitCode;
}

private int ListFilters(FilterListOptions opts, string verb)
{
int exitCode = opts.Handle(manager, user);
if (exitCode != Exit.OK)
{
return exitCode;
}

var cfg = ServiceLocator.Container.Resolve<Configuration.IConfiguration>();
user.RaiseMessage("Global filters:");
foreach (string filter in cfg.GlobalInstallFilters)
{
user.RaiseMessage("\t- {0}", filter);
}
user.RaiseMessage("");

var instance = MainClass.GetGameInstance(manager);
user.RaiseMessage("Instance filters:");
foreach (string filter in instance.InstallFilters)
{
user.RaiseMessage("\t- {0}", filter);
}
return Exit.OK;
}

private int AddFilters(FilterAddOptions opts, string verb)
{
if (opts.filters.Count < 1)
{
user.RaiseMessage("Usage: ckan filter {0} filter1 [filter2 ...]", verb);
return Exit.BADOPT;
}

int exitCode = opts.Handle(manager, user);
if (exitCode != Exit.OK)
{
return exitCode;
}

if (opts.global)
{
var cfg = ServiceLocator.Container.Resolve<Configuration.IConfiguration>();
var duplicates = cfg.GlobalInstallFilters
.Intersect(opts.filters)
.ToArray();
if (duplicates.Length > 0)
{
user.RaiseError(
"Global filters already set: {0}",
string.Join(", ", duplicates)
);
return Exit.BADOPT;
}
else
{
cfg.GlobalInstallFilters = cfg.GlobalInstallFilters
.Concat(opts.filters)
.Distinct()
.ToArray();
}
}
else
{
var instance = MainClass.GetGameInstance(manager);
var duplicates = instance.InstallFilters
.Intersect(opts.filters)
.ToArray();
if (duplicates.Length > 0)
{
user.RaiseError(
"Instance filters already set: {0}",
string.Join(", ", duplicates)
);
return Exit.BADOPT;
}
else
{
instance.InstallFilters = instance.InstallFilters
.Concat(opts.filters)
.Distinct()
.ToArray();
}
}
return Exit.OK;
}

private int RemoveFilters(FilterRemoveOptions opts, string verb)
{
if (opts.filters.Count < 1)
{
user.RaiseMessage("Usage: ckan filter {0} filter1 [filter2 ...]", verb);
return Exit.BADOPT;
}

int exitCode = opts.Handle(manager, user);
if (exitCode != Exit.OK)
{
return exitCode;
}

if (opts.global)
{
var cfg = ServiceLocator.Container.Resolve<Configuration.IConfiguration>();
var notFound = opts.filters
.Except(cfg.GlobalInstallFilters)
.ToArray();
if (notFound.Length > 0)
{
user.RaiseError(
"Global filters not found: {0}",
string.Join(", ", notFound)
);
return Exit.BADOPT;
}
else
{
cfg.GlobalInstallFilters = cfg.GlobalInstallFilters
.Except(opts.filters)
.ToArray();
}
}
else
{
var instance = MainClass.GetGameInstance(manager);
var notFound = opts.filters
.Except(instance.InstallFilters)
.ToArray();
if (notFound.Length > 0)
{
user.RaiseError(
"Instance filters not found: {0}",
string.Join(", ", notFound)
);
return Exit.BADOPT;
}
else
{
instance.InstallFilters = instance.InstallFilters
.Except(opts.filters)
.ToArray();
}
}
return Exit.OK;
}

private GameInstanceManager manager { get; set; }
private IUser user { get; set; }

private static readonly ILog log = LogManager.GetLogger(typeof(Filter));
}

internal class FilterSubOptions : VerbCommandOptions
{
[VerbOption("list", HelpText = "List install filters")]
public FilterListOptions FilterListOptions { get; set; }

[VerbOption("add", HelpText = "Add install filters")]
public FilterAddOptions FilterAddOptions { get; set; }

[VerbOption("remove", HelpText = "Remove install filters")]
public FilterRemoveOptions FilterRemoveOptions { get; set; }

[HelpVerbOption]
public string GetUsage(string verb)
{
HelpText ht = HelpText.AutoBuild(this, verb);
// Add a usage prefix line
ht.AddPreOptionsLine(" ");
if (string.IsNullOrEmpty(verb))
{
ht.AddPreOptionsLine("ckan filter - View or edit installation filters");
ht.AddPreOptionsLine($"Usage: ckan filter <command> [options]");
}
else
{
ht.AddPreOptionsLine("filter " + verb + " - " + GetDescription(verb));
switch (verb)
{
case "list":
ht.AddPreOptionsLine($"Usage: ckan filter {verb}");
break;

case "add":
ht.AddPreOptionsLine($"Usage: ckan filter {verb} [options] filter1 [filter2 ...]");
break;

case "remove":
ht.AddPreOptionsLine($"Usage: ckan filter {verb} [options] filter1 [filter2 ...]");
break;
}
}
return ht;
}
}

internal class FilterListOptions : InstanceSpecificOptions
{
}

internal class FilterAddOptions : InstanceSpecificOptions
{
[Option("global", DefaultValue = false, HelpText = "Add global filters")]
public bool global { get; set; }

[ValueList(typeof(List<string>))]
public List<string> filters { get; set; }
}

internal class FilterRemoveOptions : InstanceSpecificOptions
{
[Option("global", DefaultValue = false, HelpText = "Remove global filters")]
public bool global { get; set; }

[ValueList(typeof(List<string>))]
public List<string> filters { get; set; }
}

}
1 change: 1 addition & 0 deletions Cmdline/CKAN-cmdline.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<Compile Include="Action\Cache.cs" />
<Compile Include="Action\Compare.cs" />
<Compile Include="Action\Compat.cs" />
<Compile Include="Action\Filter.cs" />
<Compile Include="Action\ICommand.cs" />
<Compile Include="Action\Import.cs" />
<Compile Include="Action\Install.cs" />
Expand Down
6 changes: 5 additions & 1 deletion Cmdline/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,13 @@ public static int Execute(GameInstanceManager manager, CommonOptions opts, strin

case "cache":
return (new Cache()).RunSubCommand(manager, opts, new SubCommandOptions(args));

case "mark":
return (new Mark()).RunSubCommand(manager, opts, new SubCommandOptions(args));

case "filter":
return (new Filter()).RunSubCommand(manager, opts, new SubCommandOptions(args));

}
}
catch (NoGameInstanceKraken)
Expand Down
3 changes: 3 additions & 0 deletions Cmdline/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ internal class Actions : VerbCommandOptions
[VerbOption("version", HelpText = "Show the version of the CKAN client being used")]
public VersionOptions Version { get; set; }

[VerbOption("filter", HelpText = "View or edit installation filters")]
public SubCommandOptions Filter { get; set; }

[HelpVerbOption]
public string GetUsage(string verb)
{
Expand Down
2 changes: 2 additions & 0 deletions ConsoleUI/CKAN-ConsoleUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
<Compile Include="DependencyScreen.cs" />
<Compile Include="DownloadImportDialog.cs" />
<Compile Include="ExitScreen.cs" />
<Compile Include="InstallFilterAddDialog.cs" />
<Compile Include="InstallFiltersScreen.cs" />
<Compile Include="InstallScreen.cs" />
<Compile Include="GameInstanceAddScreen.cs" />
<Compile Include="GameInstanceEditScreen.cs" />
Expand Down
66 changes: 66 additions & 0 deletions ConsoleUI/InstallFilterAddDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using CKAN.Versioning;
using CKAN.Games;
using CKAN.GameVersionProviders;
using CKAN.ConsoleUI.Toolkit;
using Autofac;

namespace CKAN.ConsoleUI {

/// <summary>
/// Popup letting user enter an installation filter
/// </summary>
public class InstallFilterAddDialog : ConsoleDialog {

/// <summary>
/// Initialize the popup
/// </summary>
public InstallFilterAddDialog() : base()
{
int l = GetLeft(),
r = GetRight();
int t = GetTop(),
b = t + 4;
SetDimensions(l, t, r, b);

manualEntry = new ConsoleField(
l + 2, b - 2, r - 2
) {
GhostText = () => "<Enter a filter>"
};
AddObject(manualEntry);
manualEntry.AddTip("Enter", "Accept value");
manualEntry.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
choice = manualEntry.Value;
return false;
});

AddTip("Esc", "Cancel");
AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
choice = null;
return false;
});

CenterHeader = () => "Add Filter";
}

/// <summary>
/// Display the dialog and handle its interaction
/// </summary>
/// <param name="process">Function to control the dialog, default is normal user interaction</param>
/// <param name="theme">The visual theme to use to draw the dialog</param>
/// <returns>
/// User input
/// </returns>
public new string Run(ConsoleTheme theme, Action<ConsoleTheme> process = null)
{
base.Run(theme, process);
return choice;
}

private ConsoleField manualEntry;
private string choice;
}
}