Commandline: Upgrading from 1.9.71 to 2.2.1

Created on 15 Jan 2018  Â·  25Comments  Â·  Source: commandlineparser/commandline

According to Semantic Versioning:

MAJOR version when you make incompatible API changes,

I want to update my package:

commandline

It is going a major version number change. Is my project not going to compile? I can't see the changelog.

Most helpful comment

I did not really see a migration document. Just a new set of instructions and a sample project. I was getting a bit lost. I got it to compile but then it was throwing an error so for now I have had to revert back to the last version.

The options side of things was unaffected for me I think:

    // Define a class to receive parsed values
    class Options
    {
        [Option('l', "list", Required = false,  HelpText = "Builds a list of available calendars. Eg: -l \".\\CalendarList.xml\"")]
        public string CalendarListPath { get; set; }

        [Option('m', "mode", Required = false, HelpText = "Type of events to add to the calendar. Modes: mwb. Eg: -m mwb")]
        public string CalendarEventsMode { get; set; }

        [Option('p', "path", Required = false, HelpText = "Path to the events data file. Eg: -p \".\\EventsToAdd.xml\"")]
        public string CalendarEventsPath { get; set; }

        [Option('t', "tokencache", Required = true, HelpText = "The location of the token cache data file. Eg: -t \"file.dat\"")]
        public string TokenCachePath { get; set; }

        [Option('e', "errorlog", Required = true, HelpText = "The location for the error log. Eg: -e \"<path to error folder>\"")]
        public string ErrorLogFolder { get; set; }

        [Option('s', "signout", Required = false, HelpText = "Sign out from Outlook calendar")]
        public bool SignOut { get; set; }

        [ParserState]
        public IParserState LastParserState { get; set; }

        [HelpOption]
        public string GetUsage()
        {
            return HelpText.AutoBuild(this,
              (HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));
        }
    }

The bit that would not compile there was:

        [ParserState]
        public IParserState LastParserState { get; set; }

       [HelpOption]
        public string GetUsage()
        {
            return HelpText.AutoBuild(this,
              (HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));
        }

I removed the ParserState lines of code. Don't know the implication.

I read that the system would simply display generic help so I just removed that code too. There is a sample application which shows some of the principles. Eg:

https://github.com/commandlineparser/commandline_sampleapp/blob/master/sampleapps/quickstart_app/Program.cs

But I couldn't quoite work out how I should correctly change my own code to work with that. Mine:

        static int Main(string[] args)
        {
             try
            {
                if (CommandLine.Parser.Default.ParseArguments(args, Options))
                {
                    SimpleLog.SetLogFile(logDir: Program.Options.ErrorLogFolder, check: false);

                    var program = new Program();
                    var task = Task.Run(program.Run);
                    task.Wait();

                    // We don't need the file anymore
                    if(File.Exists(Options.CalendarEventsPath))
                        File.Delete(Options.CalendarEventsPath);
                }
                else
                {
                    ReturnErrorCode = ErrorCode.CommandLineArguments;
                }
            }
            catch(Exception ex)
            {
                // The problem I have is if the command line parser returns false or
                // raises an exception (argument null). If the log path has not been
                // specified then it will default to the "current working folder".
                // So we display the log folder in the console window. 
                SimpleLog.Log(ex);
                Console.WriteLine($"OutlookCalIFConsole: See error logs. Folder: {SimpleLog.LogDir}");
                ReturnErrorCode = ErrorCode.CommandLineArguments;
            }

            return (int)ReturnErrorCode;
        }

Their sample:

            static void Main(string[] args)
        {
            CommandLine.Parser.Default.ParseArguments<Options>(args)
              .WithParsed<Options>(opts => RunOptionsAndReturnExitCode(opts))
              .WithNotParsed<Options>((errs) => HandleParseError(errs));
        }

        static int RunOptionsAndReturnExitCode(Options options)
        {

            return 0;
        }

        static void HandleParseError(IEnumerable<Error> errs)
        {

        }

I tried to add similar methods. I must have done something wrong.

A straightforward migration like for like would be great for me. I am lost with all these Verbs and things.

All 25 comments

Possibly not, I'm in the middle of actively upgrading a couple of projects and I'm running into a few changes. I'd say just upgrade and try to compile, see what needs to change.

Speaking of which, is there a migration doc somewhere explaining the changes from 1.9.X to 2.X? I figure I'm just overlooking it somewhere.

I did not really see a migration document. Just a new set of instructions and a sample project. I was getting a bit lost. I got it to compile but then it was throwing an error so for now I have had to revert back to the last version.

The options side of things was unaffected for me I think:

    // Define a class to receive parsed values
    class Options
    {
        [Option('l', "list", Required = false,  HelpText = "Builds a list of available calendars. Eg: -l \".\\CalendarList.xml\"")]
        public string CalendarListPath { get; set; }

        [Option('m', "mode", Required = false, HelpText = "Type of events to add to the calendar. Modes: mwb. Eg: -m mwb")]
        public string CalendarEventsMode { get; set; }

        [Option('p', "path", Required = false, HelpText = "Path to the events data file. Eg: -p \".\\EventsToAdd.xml\"")]
        public string CalendarEventsPath { get; set; }

        [Option('t', "tokencache", Required = true, HelpText = "The location of the token cache data file. Eg: -t \"file.dat\"")]
        public string TokenCachePath { get; set; }

        [Option('e', "errorlog", Required = true, HelpText = "The location for the error log. Eg: -e \"<path to error folder>\"")]
        public string ErrorLogFolder { get; set; }

        [Option('s', "signout", Required = false, HelpText = "Sign out from Outlook calendar")]
        public bool SignOut { get; set; }

        [ParserState]
        public IParserState LastParserState { get; set; }

        [HelpOption]
        public string GetUsage()
        {
            return HelpText.AutoBuild(this,
              (HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));
        }
    }

The bit that would not compile there was:

        [ParserState]
        public IParserState LastParserState { get; set; }

       [HelpOption]
        public string GetUsage()
        {
            return HelpText.AutoBuild(this,
              (HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));
        }

I removed the ParserState lines of code. Don't know the implication.

I read that the system would simply display generic help so I just removed that code too. There is a sample application which shows some of the principles. Eg:

https://github.com/commandlineparser/commandline_sampleapp/blob/master/sampleapps/quickstart_app/Program.cs

But I couldn't quoite work out how I should correctly change my own code to work with that. Mine:

        static int Main(string[] args)
        {
             try
            {
                if (CommandLine.Parser.Default.ParseArguments(args, Options))
                {
                    SimpleLog.SetLogFile(logDir: Program.Options.ErrorLogFolder, check: false);

                    var program = new Program();
                    var task = Task.Run(program.Run);
                    task.Wait();

                    // We don't need the file anymore
                    if(File.Exists(Options.CalendarEventsPath))
                        File.Delete(Options.CalendarEventsPath);
                }
                else
                {
                    ReturnErrorCode = ErrorCode.CommandLineArguments;
                }
            }
            catch(Exception ex)
            {
                // The problem I have is if the command line parser returns false or
                // raises an exception (argument null). If the log path has not been
                // specified then it will default to the "current working folder".
                // So we display the log folder in the console window. 
                SimpleLog.Log(ex);
                Console.WriteLine($"OutlookCalIFConsole: See error logs. Folder: {SimpleLog.LogDir}");
                ReturnErrorCode = ErrorCode.CommandLineArguments;
            }

            return (int)ReturnErrorCode;
        }

Their sample:

            static void Main(string[] args)
        {
            CommandLine.Parser.Default.ParseArguments<Options>(args)
              .WithParsed<Options>(opts => RunOptionsAndReturnExitCode(opts))
              .WithNotParsed<Options>((errs) => HandleParseError(errs));
        }

        static int RunOptionsAndReturnExitCode(Options options)
        {

            return 0;
        }

        static void HandleParseError(IEnumerable<Error> errs)
        {

        }

I tried to add similar methods. I must have done something wrong.

A straightforward migration like for like would be great for me. I am lost with all these Verbs and things.

Is there any update on this matter? I reverted back to the old library because of the issues I encountered.

I agree the structure of the new api seems a little odd to me. It appears that in order to use it you are required to nest calls for via lambdas for a successful parse or unsuccessful parse. This seems to obfuscate the normal control flow of the program. E.g. parse the arguements, if not successful display the help text and return otherwise proceed executing Main(). Unless I am missing something, it looks like I will continue using the older version as the api is much cleaner.

I used the following approach which is buried in the wiki:
c# public static void Main(string[] args) { Parser.Default.ParseArguments<Options>(args) .MapResult( options => RunAndReturnExitCode(options), _ => 1 ); // This will automatically print an error message based on the meta data if the argument are incorrect } public static int RunAndReturnExitCode(ApplicationArguments args) { // add your code here return 0; }

Yes, I found this. But I struggle to get that to work with the code that I have. The whole thing blew up. 😞


From: John Donaghy notifications@github.com
Sent: 26 February 2018 16:24
To: commandlineparser/commandline
Cc: ajtruckle; Author
Subject: Re: [commandlineparser/commandline] Upgrading from 1.9.71 to 2.2.1 (#225)

I used the following approach which is buried in the wiki:

public static void Main(string[] args)
{
Parser.Default.ParseArguments(args)
.MapResult(
options => RunAndReturnExitCode(options),
_ => 1 ); // This will automatically print an error message based on the meta data if the argument are incorrect
}
public static int RunAndReturnExitCode(ApplicationArguments args)
{
// add your code here
return 0;
}

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHubhttps://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fcommandlineparser%2Fcommandline%2Fissues%2F225%23issuecomment-368558803&data=02%7C01%7C%7C70e22da99b1f47ae4b2308d57d3563c7%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636552590607548717&sdata=I1%2FglwuKaY19g7fClY%2FdlKDsD0YhF0%2B9PW%2BDOy1SrcM%3D&reserved=0, or mute the threadhttps://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAHnYs4Z2VoOejj9or5fszbRPzywjk_80ks5tYtpUgaJpZM4Re-JY&data=02%7C01%7C%7C70e22da99b1f47ae4b2308d57d3563c7%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636552590607548717&sdata=0oBrqKZGCzn7IWfZu5yB%2FvuC7R15AdJuwohciYLtjjM%3D&reserved=0.

Did you manage to get a simple example working? How exactly did it blow up?

It was a few months ago now and I can't remember. I currently have this set of options:

namespace OutlookCalIFConsole
{
// Define a class to receive parsed values
class Options
{
[Option('l', "list", Required = false, HelpText = "Builds a list of available calendars. Eg: -l \".\CalendarList.xml\"")]
public string CalendarListPath { get; set; }

    [Option('m', "mode", Required = false, HelpText = "Type of events to add to the calendar. Modes: mwb. Eg: -m mwb")]
    public string CalendarEventsMode { get; set; }

    [Option('p', "path", Required = false, HelpText = "Path to the events data file. Eg: -p \".\\EventsToAdd.xml\"")]
    public string CalendarEventsPath { get; set; }

    [Option('t', "tokencache", Required = true, HelpText = "The location of the token cache data file. Eg: -t \"file.dat\"")]
    public string TokenCachePath { get; set; }

    [Option('e', "errorlog", Required = true, HelpText = "The location for the error log. Eg: -e \"<path to error folder>\"")]
    public string ErrorLogFolder { get; set; }

    [Option('s', "signout", Required = false, HelpText = "Sign out from Outlook calendar")]
    public bool SignOut { get; set; }

    [ParserState]
    public IParserState LastParserState { get; set; }

    [HelpOption]
    public string GetUsage()
    {
        return HelpText.AutoBuild(this,
          (HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));
    }
}

In my main application class I had a public, static variable:

public static Options Options = new Options();

The main method was written like this:

    static int Main(string[] args)
    {
         try
        {
            if (CommandLine.Parser.Default.ParseArguments(args, Options))
            {
                SimpleLog.SetLogFile(logDir: Program.Options.ErrorLogFolder, check: false);

                var program = new Program();
                var task = Task.Run(program.Run);
                task.Wait();

                // We don't need the file anymore
                if(File.Exists(Options.CalendarEventsPath))
                    File.Delete(Options.CalendarEventsPath);
            }
            else
            {
                ReturnErrorCode = ErrorCode.CommandLineArguments;
            }
        }
        catch(Exception ex)
        {
            // The problem I have is if the command line parser returns false or
            // raises an exception (argument null). If the log path has not been
            // specified then it will default to the "current working folder".
            // So we display the log folder in the console window.
            SimpleLog.Log(ex);
            Console.WriteLine($"OutlookCalIFConsole: See error logs. Folder: {SimpleLog.LogDir}");
            ReturnErrorCode = ErrorCode.CommandLineArguments;
        }

        return (int)ReturnErrorCode;
    }

I think if you scroll up the discussion thread you will see my code and the questions that I raised at the time.

Andrew


From: John Donaghy notifications@github.com
Sent: 26 February 2018 16:45
To: commandlineparser/commandline
Cc: ajtruckle; Author
Subject: Re: [commandlineparser/commandline] Upgrading from 1.9.71 to 2.2.1 (#225)

Did you manage to get a simple example working? How exactly did it blow up?

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHubhttps://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fcommandlineparser%2Fcommandline%2Fissues%2F225%23issuecomment-368566608&data=02%7C01%7C%7Cc8de5e07dd404880492e08d57d385434%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636552603238529349&sdata=ZDX6CDBU0ip6Ah%2FpFHXIMTKwJ2JPRGBmam0qNxGtEvM%3D&reserved=0, or mute the threadhttps://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAHnYszIFI1d78_ym9STX0xUZhtltyOCPks5tYt-hgaJpZM4Re-JY&data=02%7C01%7C%7Cc8de5e07dd404880492e08d57d385434%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636552603238529349&sdata=iB%2FtZmpK1JlMBW6UlN7LhS%2F0tg4dui6PxYofXeEaGWU%3D&reserved=0.

Personally, I wouldn't wrap the whole thing in a try...catch block. I would let the Parser just handle the parsing of the arguments, and the printing of an error message if they're incorrect (which you can of course customize). You can't expect your program to always know where to print errors if the path of the error file is being passed in as an argument.

My program is a command line utility that is called behind the scenes by another executable. It isn't overly complex. I had to pass the path because of just that issue. There is a dedicated path reserved for logs inside my main executable user application data folder. And I make sure I pass this when I invoke the command line utility.

Andy


From: John Donaghy notifications@github.com
Sent: 26 February 2018 17:12
To: commandlineparser/commandline
Cc: ajtruckle; Author
Subject: Re: [commandlineparser/commandline] Upgrading from 1.9.71 to 2.2.1 (#225)

Personally, I wouldn't wrap the whole thing in a try...catch block. I would let the Parser just handle the parsing of the arguments, and the printing of an error message if they're incorrect (which you can of course customize). You can't expect your program to always know where to print errors if the path of the error file is being passed in as an argument.

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHubhttps://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fcommandlineparser%2Fcommandline%2Fissues%2F225%23issuecomment-368575715&data=02%7C01%7C%7C33dadee50d9941cf333008d57d3c1a72%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636552619442953481&sdata=%2BoGYs4S4F0vDGBjJXomj8Uln1rKrBWMjbZXCfl8Mg8A%3D&reserved=0, or mute the threadhttps://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAHnYs46pceOi9pH88g4Yh40B1SbIi-B9ks5tYuXmgaJpZM4Re-JY&data=02%7C01%7C%7C33dadee50d9941cf333008d57d3c1a72%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636552619442953481&sdata=wmY6Mzs%2F4pFseYsIK6BaD8OvNtabS0nODHapbbiqI7U%3D&reserved=0.

I've only started looking at the new API and it is definitely confusing. There does not seem to be a way to meet your needs which, if I understand correctly, requires being able to access the parsed object in the case where there are failures. This would allow you to get the path of the log file even if other arguments weren't provided.

Yes. So I am frustrated that this upgrade is so fundamentally code breaking for me.

What we had before worked and I understood. This new way I don’t for my needs. And I don’t know how to change it to accommodate the new dll.

Sent from my iPhone

On 26 Feb 2018, at 18:18, John Donaghy <[email protected]notifications@github.com> wrote:

I've only started looking at the new API and it is definitely confusing. There does not seem to be a way to meet your needs which, if I understand correctly, requires being able to access the parsed object in the case where there are failures. This would allow you to get the path of the log file even if other arguments weren't provided.

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHubhttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fcommandlineparser%2Fcommandline%2Fissues%2F225%23issuecomment-368597055&data=02%7C01%7C%7C3a4a3a13a3464aa9601d08d57d455689%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636552659105077976&sdata=52iPYe3nd%2F0Vzw0tpajbHnpW8doegU0m3XTSsQaQIl8%3D&reserved=0, or mute the threadhttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAHnYs5pnVj707HbNOFUTbrlBKy8gRCBjks5tYvVzgaJpZM4Re-JY&data=02%7C01%7C%7C3a4a3a13a3464aa9601d08d57d455689%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636552659105077976&sdata=Zh8cqXk%2BtxF3hH3GlsLVCzAa66qJ0J5XCRqRdWg6SO4%3D&reserved=0.

I'm in this exact position, it's extremely frustrating and i also don't know where to go from here. I have a library that has to be ported and the non existent documentation is not helping.

Got to a solution that works using the example from @GuyTe in this issue

Instead of having a [HelpOption] GetUsage method in your options you can print the HelpText directly in the WithNotParsed anonymous function you declare, and do what you need to in the WithParsed. You can also check the result of the parse by looking at the parseResults tag, would look something like: if(parseResult.Tag == ParserResultType.Parsed)

I simply broke out what i previously did into a method and called it with the parsed Options as a parameter like so parserResult.WithParsed<MyOptions>(options => DoSomething(options));. I'm quite rushed while writing this so if you want me to clarify something or try to do a better example with your code let me know.

A fleshed out example would help me if you have the time. 😊

Thank you!


From: Dundret notifications@github.com
Sent: 28 February 2018 15:46
To: commandlineparser/commandline
Cc: ajtruckle; Author
Subject: Re: [commandlineparser/commandline] Upgrading from 1.9.71 to 2.2.1 (#225)

Got to a solution that works using the example from @GuyTehttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fguyte&data=02%7C01%7C%7Ccb2a63970f534ed7525a08d57ec26da0%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636554295876337749&sdata=YDeLBTx7v9pH3f5wKRJY9EELpBU7%2FXTxAiDqqbVIB70%3D&reserved=0 in this issue https://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fcommandlineparser%2Fcommandline%2Fissues%2F182&data=02%7C01%7C%7Ccb2a63970f534ed7525a08d57ec26da0%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636554295876337749&sdata=eAGZ4%2FnGryLLzQ0rU7MAj%2Bs2da%2FcVWoZVm3Mssysxxs%3D&reserved=0

Instead of having a [HelpOption] GetUsage method in your options you can print the HelpText directly in the WithNotParsed anonymous function you declare. And do what you want you need to in the WithParsed. I simply broke out what i previously did into a method and called it with the parsed Options as a parameter like so parserResult.WithParsed(options => DoSomething(options));. I'm quite rushed while writing this so if you want to clarify something or try to do a better example with your code let me know.

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHubhttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fcommandlineparser%2Fcommandline%2Fissues%2F225%23issuecomment-369279340&data=02%7C01%7C%7Ccb2a63970f534ed7525a08d57ec26da0%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636554295876337749&sdata=umC3uAFgTbBmmyQRLrzwGUhtBLJ%2BubEdRJerzABbi1A%3D&reserved=0, or mute the threadhttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAHnYs6r1EZCMXMPcJxKgRRtCnQGjgUAYks5tZXNfgaJpZM4Re-JY&data=02%7C01%7C%7Ccb2a63970f534ed7525a08d57ec26da0%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636554295876337749&sdata=JBX8uzKS3zz6tuPGHCmHxvLh3HMAbaDzJKz4%2BOU548A%3D&reserved=0.

Made an attempt for a better example here, haven't run it so might be some small issue but you should get the gist. And you could probably improve the error handling by some experimentation. Hope it helps.

Edit: The logic around your ReturnErrorCode has to be updated to fit the new pattern.

Main Method:

static int Main(string[] args)
        {
        var parserResult = CommandLine.Parser.Default.ParseArguments<Options/*Or whatever you've named your options class*/>(args);
        if(parseResult.Tag == ParserResultType.NotParsed) //Move this to where it fits
        {
            ReturnErrorCode = ErrorCode.CommandLineArguments;
        }

        parserResult.WithParsed(options => OnSuccessfulParse(options));
        parserResult.WithNotParsed(errs => 
                    {
                        var helpText = HelpText.AutoBuild(parserResult, h => 
                        {
                            return HelpText.DefaultParsingErrorsHandler(parserResult, h); //This you previously did in the GetUsage method.
                        }, e => 
                        {
                            return e;
                        });
                        Console.WriteLine(helpText);
                    }
                    );

        }

Method with your logic:

private static void OnSuccessfulParse(Options options) //The code you run on a successful parse, can return an int if needed with your ReturnErrorCode logic.
    {
        try
        {
            SimpleLog.SetLogFile(logDir: Program.Options.ErrorLogFolder, check: false);

            var program = new Program();
            var task = Task.Run(program.Run);
            task.Wait();

            // We don't need the file anymore
            if(File.Exists(Options.CalendarEventsPath))
                File.Delete(Options.CalendarEventsPath);
        }
        catch(Exception ex)
        {
            // The problem I have is if the command line parser returns false or
            // raises an exception (argument null). If the log path has not been
            // specified then it will default to the "current working folder".
            // So we display the log folder in the console window. 
            SimpleLog.Log(ex);
            Console.WriteLine($"OutlookCalIFConsole: See error logs. Folder: {SimpleLog.LogDir}");
            ReturnErrorCode = ErrorCode.CommandLineArguments;

            return (int)ReturnErrorCode;
        }
    }

It compiles, thanks. But for some reason line:

if(parserResult.Tag == ParserResultType.NotParsed) //Move this to where it fits

Is always true.

Yes, for some reason once it has parsed my arguments my Options featur eis not filled.

I think I got it. Firstly, I tweaked the code:

        static int Main(string[] args)
        {
            Console.WriteLine(args.ToString());
            var parserResult = CommandLine.Parser.Default.ParseArguments<Options>(args);
            if (parserResult.Tag == ParserResultType.NotParsed)
            {
                ReturnErrorCode = ErrorCode.CommandLineArguments;
            }

            parserResult.WithParsed<Options>(options => OnSuccessfulParse(options));
            parserResult.WithNotParsed<Options>(errs =>
            {
                var helpText = HelpText.AutoBuild(parserResult, h =>
                {
                    return HelpText.DefaultParsingErrorsHandler(parserResult, h);
                }, e =>
                {
                    return e;
                });
                Console.WriteLine(helpText);
            });

            return (int)ReturnErrorCode;
        }

Then I did the following:

        private static void OnSuccessfulParse(Options options)
        {
            try
            {
                Program.Options = options;

                SimpleLog.SetLogFile(logDir: Program.Options.ErrorLogFolder, check: false);

                var program = new Program();
                var task = Task.Run(program.Run);
                task.Wait();

                // We don't need the file anymore
                if (File.Exists(Options.CalendarEventsPath))
                    File.Delete(Options.CalendarEventsPath);
            }
            catch (Exception ex)
            {
                // The problem I have is if the command line parser returns false or
                // raises an exception (argument null). If the log path has not been
                // specified then it will default to the "current working folder".
                // So we display the log folder in the console window. 
                SimpleLog.Log(ex);
                Console.WriteLine($"OutlookCalIFConsole: See error logs. Folder: {SimpleLog.LogDir}");
                ReturnErrorCode = ErrorCode.CommandLineArguments;
            }
        }

As you can see, I had to copy the options over into the public options. I don't know why I had to do that? The ones passed into the method were OK and once i copied them my app worked.

Thoughts?

Hmm, i think you can remove
if (parserResult.Tag == ParserResultType.NotParsed) { ReturnErrorCode = ErrorCode.CommandLineArguments; }
And instead set the ReturnErrorCode = ErrorCode.CommandLineArguments; in the WithNotParsed.

I Would also fix the private static void OnSuccessfulParse(Options options) method to return an int, because if you look in the catch it sets the ReturnErrorCode = ErrorCode.CommandLineArguments; and then does nothing with it, after some more looking it seems the parserResult.Tag is used mostly in tests together with some other validation on the parse, the two "cases" WithParsedand WithNotParsedshould have sufficient logic to handle when the parse is successful and when it's not.

Don't know why you had to copy the options over, you should be able to access them directly, is your Options class not set to public by chance?

Other than that i think it looks fine, i would recommend doing some extensive testing to make sure it does what you expect and want it to do.

Thanks for the suggestions. I have adjusted the first bit like you said:

        static int Main(string[] args)
        {
            var parserResult = CommandLine.Parser.Default.ParseArguments<Options>(args);

            parserResult.WithParsed<Options>(options => OnSuccessfulParse(options));
            parserResult.WithNotParsed<Options>(errs =>
            {
                var helpText = HelpText.AutoBuild(parserResult, h =>
                {
                    return HelpText.DefaultParsingErrorsHandler(parserResult, h);
                }, e =>
                {
                    return e;
                });
                Console.WriteLine(helpText);
                ReturnErrorCode = ErrorCode.CommandLineArguments;
            });

            return (int)ReturnErrorCode;
        }

Out of interest, what would the code look like if we moved it into a OnUnsuccessfulParse method?

I Would also fix the private static void OnSuccessfulParse(Options options) method to return an int

This is fine I think because that variable is decalred liek this:

public static ErrorCode ReturnErrorCode = ErrorCode.NoError;

So it doesn't need to return the value.

Don't know why you had to copy the options over, you should be able to access them directly, is your Options class not set to public by chance?

Is it public. See:

public static Options Options = new Options();

Okay then the ReturnErrorCode is fine. I Didn't mean your instantiated Options variable, i meant your Options class, i scrolled up and saw you pasted it there, it's not set to public class Options, might have something to do with that but not sure, trying to think of what else could cause you not being able to access the options. I haven't tried doing a method that the WithNotParsed calls but you should be able to do the same thing as we do in the WithParsed.

Well, This is the parsing code:

        var parserResult = CommandLine.Parser.Default.ParseArguments<Options>(args);

        parserResult.WithParsed<Options>(options => OnSuccessfulParse(options));
        parserResult.WithNotParsed<Options>(errs =>

So there is no instance above where it actually populates my options variable:

       public static Options Options = new Options();

We then pass options in like this:

       parserResult.WithParsed<Options>(options => OnSuccessfulParse(options));

I am not quite sure what exactly "options" literally is here. Eitherway, it is not my own variable.

Now, I could have adjusted all my code to use the passed in "options" variable in OnSuccessfulParse but I did not see the point in changing my code for that. So I just copied it to the original public variable.


From: Dundret notifications@github.com
Sent: 02 March 2018 09:28
To: commandlineparser/commandline
Cc: ajtruckle; Author
Subject: Re: [commandlineparser/commandline] Upgrading from 1.9.71 to 2.2.1 (#225)

Okay then the ReturnErrorCode is fine. I Didn't mean your instantiated Options variable, i meant your Options class, i scrolled up and saw you pasted it there, it's not set to public class Options, might have something to do with that but not sure, trying to think of what else could cause you not being able to access the options. I haven't tried doing a method that the WithNotParsed calls but you should be able to do the same thing as we do in the WithParsed.

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHubhttps://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fcommandlineparser%2Fcommandline%2Fissues%2F225%23issuecomment-369868171&data=02%7C01%7C%7C3de1bad9194141c2cbe808d5801fe8c9%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636555796886443310&sdata=5LxvJnlXclsbMxZ4tu4TjprgcCSBZIRIop2ryVnbksk%3D&reserved=0, or mute the threadhttps://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAHnYs234L52NjmNJpdnmwb1fQeitKzC7ks5taRClgaJpZM4Re-JY&data=02%7C01%7C%7C3de1bad9194141c2cbe808d5801fe8c9%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636555796886443310&sdata=rUrwIGtL%2BYB45IEDug6GyNaZdelFNqasI2zvgB0TFeM%3D&reserved=0.

True true, well then it works and if you're satisfied you can mark another one done on ye old task board ;)

What about an extension?

public static class CommandLineParserEx { public static void ParseArguments<T>(this Parser parser, IEnumerable<string> args, out T options) { T opts= default(T); parser.ParseArguments<T>(args).WithParsed(o => opts = o); options = opts; } }

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ericnewton76 picture ericnewton76  Â·  4Comments

Valinwolf picture Valinwolf  Â·  3Comments

Rohansi picture Rohansi  Â·  6Comments

smorzhov picture smorzhov  Â·  4Comments

Eason-Lian picture Eason-Lian  Â·  3Comments