diff --git a/Source/LibationCli/ConsoleProgressBar.cs b/Source/LibationCli/ConsoleProgressBar.cs new file mode 100644 index 00000000..7369f427 --- /dev/null +++ b/Source/LibationCli/ConsoleProgressBar.cs @@ -0,0 +1,77 @@ +using System; +using System.IO; + +namespace LibationCli; + +internal class ConsoleProgressBar +{ + public TextWriter Output { get; } + public int MaxWidth { get; } + public char ProgressChar { get; } + public char NoProgressChar { get; } + + public double? Progress + { + get => m_Progress; + set + { + m_Progress = value ?? 0; + WriteProgress(); + } + } + + public TimeSpan RemainingTime + { + get => m_RemainingTime; + set + { + m_RemainingTime = value; + WriteProgress(); + } + } + + + private double m_Progress; + private TimeSpan m_RemainingTime; + private int m_LastWriteLength = 0; + private const int MAX_ETA_LEN = 10; + private readonly int m_NumProgressPieces; + + public ConsoleProgressBar( + TextWriter output, + int maxWidth = 80, + char progressCharr = '#', + char noProgressChar = '.') + { + Output = output; + MaxWidth = maxWidth; + ProgressChar = progressCharr; + NoProgressChar = noProgressChar; + m_NumProgressPieces = MaxWidth - MAX_ETA_LEN - 4; + } + + private void WriteProgress() + { + var numCompleted = (int)Math.Round(double.Min(100, m_Progress) * m_NumProgressPieces / 100); + var numRemaining = m_NumProgressPieces - numCompleted; + var progressBar = $"[{new string(ProgressChar, numCompleted)}{new string(NoProgressChar, numRemaining)}] "; + + progressBar += RemainingTime.TotalMinutes > 1000 + ? "ETA ∞" + : $"ETA {(int)RemainingTime.TotalMinutes}:{RemainingTime.Seconds:D2}"; + + Output.Write(new string('\b', m_LastWriteLength) + progressBar); + if (progressBar.Length < m_LastWriteLength) + { + var extra = m_LastWriteLength - progressBar.Length; + Output.Write(new string(' ', extra) + new string('\b', extra)); + } + m_LastWriteLength = progressBar.Length; + } + + public void Clear() + => Output.Write( + new string('\b', m_LastWriteLength) + + new string(' ', m_LastWriteLength) + + new string('\b', m_LastWriteLength)); +} diff --git a/Source/LibationCli/HelpVerb.cs b/Source/LibationCli/HelpVerb.cs new file mode 100644 index 00000000..b9e329cf --- /dev/null +++ b/Source/LibationCli/HelpVerb.cs @@ -0,0 +1,52 @@ +using AppScaffolding; +using CommandLine; +using CommandLine.Text; + +namespace LibationCli; + +[Verb("help", HelpText = "Display more information on a specific command.")] +internal class HelpVerb +{ + /// + /// Name of the verb to get help about + /// + [Value(0, Default = "")] + public string HelpType { get; set; } + + /// + /// Create a base for + /// + public static HelpText CreateHelpText() + { + var auto = new HelpText + { + AutoVersion = false, + AutoHelp = false, + Heading = $"LibationCli v{LibationScaffolding.BuildVersion.ToString(3)}", + AdditionalNewLineAfterOption = true, + MaximumDisplayWidth = 80 + }; + return auto; + } + + /// + /// Get the 's + /// + public HelpText GetHelpText() + { + var helpText = CreateHelpText(); + var result = new Parser().ParseArguments(new string[] { HelpType }, Program.VerbTypes); + if (result.TypeInfo.Current == typeof(NullInstance)) + { + //HelpType is not a defined verb so get LibationCli usage + helpText.AddVerbs(Program.VerbTypes); + } + else + { + helpText.AddDashesToOption = true; + helpText.AutoHelp = true; + helpText.AddOptions(result); + } + return helpText; + } +} diff --git a/Source/LibationCli/LibationCli.csproj b/Source/LibationCli/LibationCli.csproj index 04926587..864e4ec6 100644 --- a/Source/LibationCli/LibationCli.csproj +++ b/Source/LibationCli/LibationCli.csproj @@ -3,7 +3,8 @@ Exe - net7.0 + net7.0-windows + win-x64 true false false diff --git a/Source/LibationCli/Options/ConvertOptions.cs b/Source/LibationCli/Options/ConvertOptions.cs index fa3b744b..b20f0152 100644 --- a/Source/LibationCli/Options/ConvertOptions.cs +++ b/Source/LibationCli/Options/ConvertOptions.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using CommandLine; using System.Threading.Tasks; -using CommandLine; namespace LibationCli { diff --git a/Source/LibationCli/Options/ExportOptions.cs b/Source/LibationCli/Options/ExportOptions.cs index 6d43a029..6e994718 100644 --- a/Source/LibationCli/Options/ExportOptions.cs +++ b/Source/LibationCli/Options/ExportOptions.cs @@ -1,10 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ApplicationServices; -using AudibleUtilities; +using ApplicationServices; using CommandLine; +using System; +using System.IO; +using System.Threading.Tasks; namespace LibationCli { @@ -29,26 +27,38 @@ namespace LibationCli } */ #endregion - [Option(shortName: 'x', longName: "xlsx", SetName = "xlsx", Required = true)] + [Option(shortName: 'x', longName: "xlsx", HelpText = "Microsoft Excel Spreadsheet", SetName = "Export Format")] public bool xlsx { get; set; } - [Option(shortName: 'c', longName: "csv", SetName = "csv", Required = true)] + [Option(shortName: 'c', longName: "csv", HelpText = "Comma-separated values", SetName = "Export Format")] public bool csv { get; set; } - [Option(shortName: 'j', longName: "json", SetName = "json", Required = true)] + [Option(shortName: 'j', longName: "json", HelpText = "JavaScript Object Notation", SetName = "Export Format")] public bool json { get; set; } protected override Task ProcessAsync() { - if (xlsx) - LibraryExporter.ToXlsx(FilePath); - if (csv) - LibraryExporter.ToCsv(FilePath); - if (json) - LibraryExporter.ToJson(FilePath); - - Console.WriteLine($"Library exported to: {FilePath}"); + Action exporter + = csv ? LibraryExporter.ToCsv + : json ? LibraryExporter.ToJson + : xlsx ? LibraryExporter.ToXlsx + : Path.GetExtension(FilePath)?.ToLower() switch + { + ".xlsx" => LibraryExporter.ToXlsx, + ".csv" => LibraryExporter.ToCsv, + ".json" => LibraryExporter.ToJson, + _ => null + }; + if (exporter is null) + { + PrintVerbUsage($"Undefined export format for file type \"{Path.GetExtension(FilePath)}\""); + } + else + { + exporter(FilePath); + Console.WriteLine($"Library exported to: {FilePath}"); + } return Task.CompletedTask; } } diff --git a/Source/LibationCli/Options/LiberateOptions.cs b/Source/LibationCli/Options/LiberateOptions.cs index 5c904771..ab1a8e78 100644 --- a/Source/LibationCli/Options/LiberateOptions.cs +++ b/Source/LibationCli/Options/LiberateOptions.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using CommandLine; +using CommandLine; using DataLayer; using FileLiberator; +using System.Threading.Tasks; namespace LibationCli { diff --git a/Source/LibationCli/Options/ScanOptions.cs b/Source/LibationCli/Options/ScanOptions.cs index 5f1be8e3..43c888dd 100644 --- a/Source/LibationCli/Options/ScanOptions.cs +++ b/Source/LibationCli/Options/ScanOptions.cs @@ -1,18 +1,18 @@ -using System; +using ApplicationServices; +using AudibleUtilities; +using CommandLine; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using ApplicationServices; -using AudibleUtilities; -using CommandLine; namespace LibationCli { [Verb("scan", HelpText = "Scan library. Default: scan all accounts. Optional: use 'account' flag to specify a single account.")] public class ScanOptions : OptionsBase { - [Value(0, MetaName = "Accounts", HelpText = "Optional: nicknames of accounts to scan.", Required = false)] - public IEnumerable AccountNicknames { get; set; } + [Value(0, MetaName = "Accounts", HelpText = "Optional: user ID or nicknames of accounts to scan.", Required = false)] + public IEnumerable AccountNames { get; set; } protected override async Task ProcessAsync() { @@ -42,13 +42,19 @@ namespace LibationCli private Account[] getAccounts() { using var persister = AudibleApiStorage.GetAccountsSettingsPersister(); - var accounts = persister.AccountsSettings.GetAll().ToArray(); + var allAccounts = persister.AccountsSettings.GetAll().ToArray(); - if (!AccountNicknames.Any()) - return accounts; + if (!AccountNames.Any()) + return allAccounts; - var found = accounts.Where(acct => AccountNicknames.Contains(acct.AccountName)).ToArray(); - var notFound = AccountNicknames.Except(found.Select(f => f.AccountName)).ToArray(); + var accountNames = AccountNames.Select(n => n.ToLower()).ToArray(); + + var found + = allAccounts + .Where(acct => accountNames.Contains(acct.AccountName.ToLower()) || accountNames.Contains(acct.AccountId.ToLower())) + .ToArray(); + + var notFound = allAccounts.Except(found).ToArray(); // no accounts found. do not continue if (!found.Any()) diff --git a/Source/LibationCli/Options/SearchOptions.cs b/Source/LibationCli/Options/SearchOptions.cs new file mode 100644 index 00000000..5ed00d6a --- /dev/null +++ b/Source/LibationCli/Options/SearchOptions.cs @@ -0,0 +1,50 @@ +using ApplicationServices; +using CommandLine; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace LibationCli.Options +{ + [Verb("search", HelpText = "Search for books in your library")] + internal class SearchOptions : OptionsBase + { + [Value(0, MetaName = "query", Required = true, HelpText = "Lucene query test to search")] + public IEnumerable Query { get; set; } + + protected override Task ProcessAsync() + { + var query = string.Join(" ", Query).Trim('\"'); + var results = SearchEngineCommands.Search(query).Docs.ToList(); + + Console.WriteLine($"Found {results.Count} matching results."); + + const string nextPrompt = "Press any key for the next 10 results or Esc for all results"; + bool waitForNextBatch = true; + + for (int i = 0; i < results.Count; i += 10) + { + foreach (var doc in results.Skip(i).Take(10)) + Console.WriteLine(getDocDisplay(doc.Doc)); + + if (waitForNextBatch) + { + Console.Write(nextPrompt); + waitForNextBatch = Console.ReadKey().Key != ConsoleKey.Escape; + ReplaceConsoleText(Console.Out, nextPrompt.Length, ""); + Console.SetCursorPosition(0, Console.CursorTop); + } + } + + return Task.CompletedTask; + } + + private static string getDocDisplay(Lucene.Net.Documents.Document doc) + { + var title = doc.GetField("title"); + var id = doc.GetField("_ID_"); + return $"[{id.StringValue}] - {title.StringValue}"; + } + } +} diff --git a/Source/LibationCli/Options/SetDownloadStatusOptions.cs b/Source/LibationCli/Options/SetDownloadStatusOptions.cs index 0bde6a84..1c5f43ac 100644 --- a/Source/LibationCli/Options/SetDownloadStatusOptions.cs +++ b/Source/LibationCli/Options/SetDownloadStatusOptions.cs @@ -1,37 +1,69 @@ -using System; +using ApplicationServices; +using CommandLine; +using DataLayer; +using Dinah.Core; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using ApplicationServices; -using AudibleUtilities; -using CommandLine; namespace LibationCli { - [Verb("set-status", HelpText = """ - Set download statuses throughout library based on whether each book's audio file can be found. - Must include at least one flag: --downloaded , --not-downloaded. - Downloaded: If the audio file can be found, set download status to 'Downloaded'. - Not Downloaded: If the audio file cannot be found, set download status to 'Not Downloaded' + [Verb("set-status", HelpText = """ + Set download statuses throughout library based on whether each book's audio file can be found. """)] - public class SetDownloadStatusOptions : OptionsBase - { - [Option(shortName: 'd', longName: "downloaded", Required = true)] - public bool SetDownloaded { get; set; } + public class SetDownloadStatusOptions : OptionsBase + { + [Option(shortName: 'd', longName: "downloaded", Group = "Download Status", HelpText = "set download status to 'Downloaded'")] + public bool SetDownloaded { get; set; } - [Option(shortName: 'n', longName: "not-downloaded", Required = true)] - public bool SetNotDownloaded { get; set; } + [Option(shortName: 'n', longName: "not-downloaded", Group = "Download Status", HelpText = "set download status to 'Downloaded'")] + public bool SetNotDownloaded { get; set; } - protected override async Task ProcessAsync() - { - var libraryBooks = DbContexts.GetLibrary_Flat_NoTracking(); + [Option("force", HelpText = "Set the download status regardless of whether the book's audio file can be found. Only one download status option may be used with this option.")] + public bool Force { get; set; } - var bulkSetStatus = new BulkSetDownloadStatus(libraryBooks, SetDownloaded, SetNotDownloaded); - await Task.Run(() => bulkSetStatus.Discover()); - bulkSetStatus.Execute(); + [Value(0, MetaName = "[asins]", HelpText = "Optional product IDs of books on which to set download status.")] + public IEnumerable Asins { get; set; } - foreach (var msg in bulkSetStatus.Messages) - Console.WriteLine(msg); - } - } + protected override async Task ProcessAsync() + { + if (Force && SetDownloaded && SetNotDownloaded) + { + PrintVerbUsage("ERROR:\nWhen run with --force option, only one download status option may be used."); + return; + } + + var libraryBooks = DbContexts.GetLibrary_Flat_NoTracking(); + + if (Asins.Any()) + { + var asins = Asins.Select(a => a.ToLower()).ToArray(); + libraryBooks = libraryBooks.Where(lb => lb.Book.AudibleProductId.ToLower().In(asins)).ToList(); + + if (libraryBooks.Count == 0) + { + Console.Error.WriteLine("Could not find any books matching asins"); + return; + } + } + + if (Force) + { + var status = SetDownloaded ? LiberatedStatus.Liberated : LiberatedStatus.NotLiberated; + + var num = libraryBooks.UpdateBookStatus(status); + Console.WriteLine($"Set LiberatedStatus to '{status}' on {"book".PluralizeWithCount(num)}"); + } + else + { + var bulkSetStatus = new BulkSetDownloadStatus(libraryBooks, SetDownloaded, SetNotDownloaded); + await Task.Run(() => bulkSetStatus.Discover()); + bulkSetStatus.Execute(); + + foreach (var msg in bulkSetStatus.Messages) + Console.WriteLine(msg); + } + } + } } diff --git a/Source/LibationCli/Options/VersionOptions.cs b/Source/LibationCli/Options/VersionOptions.cs new file mode 100644 index 00000000..4ebced9e --- /dev/null +++ b/Source/LibationCli/Options/VersionOptions.cs @@ -0,0 +1,59 @@ +using AppScaffolding; +using CommandLine; +using System; +using System.Threading.Tasks; + +namespace LibationCli.Options; + +[Verb("version", HelpText = "Display version information.")] +internal class VersionOptions : OptionsBase +{ + [Option('c', "check", Required = false, HelpText = "Check if an upgrade is available")] + public bool CheckForUpgrade { get; set; } + + protected override Task ProcessAsync() + { + const string checkingForUpgrade = "Checking for upgrade..."; + Console.WriteLine($"Libation {LibationScaffolding.Variety} v{LibationScaffolding.BuildVersion.ToString(3)}"); + + if (CheckForUpgrade) + { + Console.Write(checkingForUpgrade); + + var origColor = Console.ForegroundColor; + try + { + var upgradeProperties = LibationScaffolding.GetLatestRelease(); + + if (upgradeProperties is null) + { + Console.ForegroundColor = ConsoleColor.Green; + ReplaceConsoleText(Console.Out, checkingForUpgrade.Length, "No available upgrade"); + Console.WriteLine(); + } + else + { + Console.ForegroundColor = ConsoleColor.Red; + ReplaceConsoleText(Console.Out, checkingForUpgrade.Length, $"Upgrade Available: v{upgradeProperties.LatestRelease.ToString(3)}"); + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine(upgradeProperties.ZipUrl); + Console.WriteLine(); + Console.WriteLine("Release Notes"); + Console.WriteLine("============="); + Console.WriteLine(upgradeProperties.Notes); + } + } + catch + { + Console.Error.WriteLine("ERROR CHECKING FOR UPGRADE"); + } + finally + { + Console.ForegroundColor = origColor; + } + } + + return Task.CompletedTask; + } +} diff --git a/Source/LibationCli/Options/_OptionsBase.cs b/Source/LibationCli/Options/_OptionsBase.cs index 40578b75..cce3f0ee 100644 --- a/Source/LibationCli/Options/_OptionsBase.cs +++ b/Source/LibationCli/Options/_OptionsBase.cs @@ -1,8 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using CommandLine; +using System; +using System.IO; +using System.Reflection; using System.Threading.Tasks; -using CommandLine; namespace LibationCli { @@ -17,15 +17,34 @@ namespace LibationCli catch (Exception ex) { Environment.ExitCode = (int)ExitCode.RunTimeError; - - Console.Error.WriteLine("ERROR"); - Console.Error.WriteLine("====="); - Console.Error.WriteLine(ex.Message); - Console.Error.WriteLine(); - Console.Error.WriteLine(ex.StackTrace); + PrintVerbUsage(new string[] + { + "ERROR", + "=====", + ex.Message, + "", + ex.StackTrace + }); } } + protected void PrintVerbUsage(params string[] linesBeforeUsage) + { + var verb = GetType().GetCustomAttribute().Name; + var helpText = new HelpVerb { HelpType = verb }.GetHelpText(); + helpText.AddPreOptionsLines(linesBeforeUsage); + helpText.AddPreOptionsLine(""); + helpText.AddPreOptionsLine($"{verb} Usage:"); + Console.Error.WriteLine(helpText); + } + + protected static void ReplaceConsoleText(TextWriter writer, int previousLength, string newText) + { + writer.Write(new string('\b', previousLength)); + writer.Write(newText); + writer.Write(new string(' ', int.Max(0, previousLength - newText.Length))); + } + protected abstract Task ProcessAsync(); } } diff --git a/Source/LibationCli/Options/_ProcessableOptionsBase.cs b/Source/LibationCli/Options/_ProcessableOptionsBase.cs index 664499c1..bb4cd735 100644 --- a/Source/LibationCli/Options/_ProcessableOptionsBase.cs +++ b/Source/LibationCli/Options/_ProcessableOptionsBase.cs @@ -1,23 +1,34 @@ -using System; +using ApplicationServices; +using CommandLine; +using DataLayer; +using Dinah.Core; +using FileLiberator; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using ApplicationServices; -using CommandLine; -using DataLayer; -using FileLiberator; namespace LibationCli { public abstract class ProcessableOptionsBase : OptionsBase { + + [Value(0, MetaName = "[asins]", HelpText = "Optional product IDs of books to process.")] + public IEnumerable Asins { get; set; } + protected static TProcessable CreateProcessable(EventHandler completedAction = null) where TProcessable : Processable, new() { + var progressBar = new ConsoleProgressBar(Console.Out); var strProc = new TProcessable(); strProc.Begin += (o, e) => Console.WriteLine($"{typeof(TProcessable).Name} Begin: {e}"); - strProc.Completed += (o, e) => Console.WriteLine($"{typeof(TProcessable).Name} Completed: {e}"); + + strProc.Completed += (o, e) => + { + progressBar.Clear(); + Console.WriteLine($"{typeof(TProcessable).Name} Completed: {e}"); + }; strProc.Completed += (s, e) => { @@ -32,12 +43,23 @@ namespace LibationCli } }; + strProc.StreamingTimeRemaining += (_, e) => progressBar.RemainingTime = e; + strProc.StreamingProgressChanged += (_, e) => progressBar.Progress = e.ProgressPercentage; + return strProc; } - protected static async Task RunAsync(Processable Processable) + protected async Task RunAsync(Processable Processable) { - foreach (var libraryBook in Processable.GetValidLibraryBooks(DbContexts.GetLibrary_Flat_NoTracking())) + var libraryBooks = DbContexts.GetLibrary_Flat_NoTracking().AsEnumerable(); + + if (Asins.Any()) + { + var asinsLower = Asins.Select(a => a.ToLower()).ToArray(); + libraryBooks = libraryBooks.Where(lb => lb.Book.AudibleProductId.ToLower().In(asinsLower)); + } + + foreach (var libraryBook in Processable.GetValidLibraryBooks(libraryBooks)) await ProcessOneAsync(Processable, libraryBook, false); var done = "Done. All books have been processed"; diff --git a/Source/LibationCli/Program.cs b/Source/LibationCli/Program.cs index 3bdce3da..19538dd5 100644 --- a/Source/LibationCli/Program.cs +++ b/Source/LibationCli/Program.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using CommandLine; +using CommandLine; using CommandLine.Text; using Dinah.Core; -using Dinah.Core.Collections; -using Dinah.Core.Collections.Generic; +using System; +using System.Linq; +using System.Threading.Tasks; namespace LibationCli { @@ -19,47 +16,63 @@ namespace LibationCli } class Program { - static async Task Main(string[] args) + public readonly static Type[] VerbTypes = Setup.LoadVerbs(); + static async Task Main(string[] args) { - //***********************************************// - // // - // do not use Configuration before this line // - // // - //***********************************************// - Setup.Initialize(); - - var types = Setup.LoadVerbs(); #if DEBUG string input = null; + //input = " set-status -n --force B017V4IM1G"; + //input = " liberate B017V4IM1G"; + //input = " convert B017V4IM1G"; + //input = " search \"-liberated\""; //input = " export --help"; + //input = " version --check"; //input = " scan rmcrackan"; + //input = " help set-status"; //input = " liberate "; - // note: this hack will fail for quoted file paths with spaces because it will break on those spaces if (!string.IsNullOrWhiteSpace(input)) args = input.Split(' ', StringSplitOptions.RemoveEmptyEntries); var setBreakPointHere = args; #endif - var result = Parser.Default.ParseArguments(args, types); + var result = new Parser(ConfigureParser).ParseArguments(args, VerbTypes); - // if successfully parsed - // async: run parsed options - await result.WithParsedAsync(opt => opt.Run()); + if (result.Value is HelpVerb helper) + Console.Error.WriteLine(helper.GetHelpText()); + else if (result.TypeInfo.Current == typeof(HelpVerb)) + { + //Error parsing the command, but the verb type was identified as HelpVerb + //Print LibationCli usage + var helpText = HelpVerb.CreateHelpText(); + helpText.AddVerbs(VerbTypes); + Console.Error.WriteLine(helpText); + } + else if (result.Errors.Any()) + HandleErrors(result); + else + { + //Everything parsed correctly, so execute the command - // if not successfully parsed - // sync: handle parse errors - result.WithNotParsed(errors => HandleErrors(result, errors)); + //***********************************************// + // // + // do not use Configuration before this line // + // // + //***********************************************// + Setup.Initialize(); - return Environment.ExitCode; + // if successfully parsed + // async: run parsed options + await result.WithParsedAsync(opt => opt.Run()); + } } - private static void HandleErrors(ParserResult result, IEnumerable errors) + private static void HandleErrors(ParserResult result) { - var errorsList = errors.ToList(); + var errorsList = result.Errors.ToList(); if (errorsList.Any(e => e.Tag.In(ErrorType.HelpRequestedError, ErrorType.VersionRequestedError, ErrorType.HelpVerbRequestedError))) { Environment.ExitCode = (int)ExitCode.NonRunNonError; @@ -67,17 +80,36 @@ namespace LibationCli } Environment.ExitCode = (int)ExitCode.ParseError; + var helpText = HelpVerb.CreateHelpText(); - if (errorsList.Any(e => e.Tag.In(ErrorType.NoVerbSelectedError))) + if (errorsList.OfType().Any()) { - Console.Error.WriteLine("No verb selected"); - return; + //Print LibationCli usage + helpText.AddPreOptionsLine("No verb selected"); + helpText.AddVerbs(VerbTypes); } + else + { + //print the specified verb's usage + helpText.AddDashesToOption = true; + helpText.AutoHelp = true; - var helpText = HelpText.AutoBuild(result, - h => HelpText.DefaultParsingErrorsHandler(result, h), - e => e); - Console.WriteLine(helpText); + if (!errorsList.OfType().Any(o => o.Token.ToLower() == "help")) + { + //verb was not executed with the "--help" option, + //so print verb option parsing error info. + helpText = HelpText.DefaultParsingErrorsHandler(result, helpText); + } + + helpText.AddOptions(result); + } + Console.Error.WriteLine(helpText); + } + + private static void ConfigureParser(ParserSettings settings) + { + settings.AutoVersion = false; + settings.AutoHelp = false; } } } diff --git a/Source/LibationCli/Setup.cs b/Source/LibationCli/Setup.cs index fc9e35ea..8c4f242e 100644 --- a/Source/LibationCli/Setup.cs +++ b/Source/LibationCli/Setup.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; +using AppScaffolding; +using CommandLine; +using System; using System.Linq; using System.Reflection; -using System.Threading.Tasks; -using AppScaffolding; -using CommandLine; -using CommandLine.Text; -using Dinah.Core; -using Dinah.Core.Collections; -using Dinah.Core.Collections.Generic; namespace LibationCli { @@ -16,6 +10,11 @@ namespace LibationCli { public static void Initialize() { + //Determine variety by the dlls present in the current directory. + //Necessary to be able to check for upgrades. + var variety = System.IO.File.Exists("System.Windows.Forms.dll") ? Variety.Classic : Variety.Chardonnay; + LibationScaffolding.SetReleaseIdentifier(variety); + //***********************************************// // // // do not use Configuration before this line // @@ -26,28 +25,6 @@ namespace LibationCli LibationScaffolding.RunPostConfigMigrations(config); LibationScaffolding.RunPostMigrationScaffolding(config); - -#if !DEBUG - checkForUpdate(); -#endif - } - - private static void checkForUpdate() - { - var upgradeProperties = LibationScaffolding.GetLatestRelease(); - if (upgradeProperties is null) - return; - - var origColor = Console.ForegroundColor; - try - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine($"UPDATE AVAILABLE @ {upgradeProperties.ZipUrl}"); - } - finally - { - Console.ForegroundColor = origColor; - } } public static Type[] LoadVerbs() => Assembly.GetExecutingAssembly()