Migrate SQLPrompt Snippets to VSCode

 I love snippets; love em. And I have a whole bunch in RedGate SQL Prompt. Now I want to be able to use those in VSCode as well, but boy do I dread having to retype all of them. Solution? Python! First arg is the path where your SQLPrompt snippets are Second arg is the directory where you want it to spit out a "sql.json" file with all your snippets. """ A script to translate sqlprompt snippet files to vscode formatted snippets """ import os import json import glob import io import argparse class SQLPromptPlaceholder :     """Represents the values of a SQLPrompt placeholder"""     def __init__ ( self , name , default_value ):         self . name = name         self . default_value = default_value class SQLPromptSnippet :     """Represents the content of a SQLPrompt snippet"""     @ staticmethod     def from_file ( filename ):         """Generates an instance fr

Boolean Command Line Options with CommandLineParser lib

So I like the CommandLineParser library for C# command line applications. One thing that tripped me up however was the handling of boolean inputs.

Most people are aware of "flags" in shell scripting. For example, in bash, "-a" is an argument for the command "ls". "-a" is conceptually a boolean flag whose presence informs the application "do not ignore entries starting with .". This is in comparison to a parameter with a value, as in "-Path" in "Get-ChildItem -Path C:\" where "C:\" is the value of the parameter "-Path". Flags are always true if present and assumed to be false if absent.

In the CommandLineParser library, you can do Flag-like parameters with the bool datatype. You can also give them a default value of either true or false (using the DefaultValue argument on the Option attribute). The problem is, if you make the default value true, you might as well not even add it as a parameter since you can never negate it. Don't believe me? 

Assume you have the following parameter:

[Option("DoSomething", DefaultValue = true)]
public bool DoSomething { get; set; }
  • If you omit this parameter, DoSomething will be true.
  • If you provide this parameter as a flag ("--DoSomething"), DoSomething will be true
  • There is no option 3. There's no way to pass a false value to DoSomething. 
    • You can pass "--DoSomething false", and it won't yell at you, but it will completely ignore what you passed in.
This leads to the implicit requirement that all flag options must be false by default. This isn't the worst thing in the world, but it can make code feel clunky. For instance, you've got an app which does some processing of a file then moves it to a "done" directory. Under most circumstances, you want that movement to take place. It's only when you're debugging, or in some niche circumstance that you don't want to move the file. Given that we just saw that flags have to default to false if you want to be able to negate them, to have the default behavior be to move the file, you'd have to name the option something like "--DoNoMoveFileUponCompletion", and then only move the file when that property is false.

Again, perhaps not the worst thing in the world, but is a fairly arbitrary constraint on how you can program your applications.

The other option is to declare the option as a nullable bool.

[Option("DoSomething", DefaultValue = false)]
public bool? DoSomething { get; set; }

However you can also make the parameter a nullable bool, which changes the behavior of the parameter a bit. 

First off, it no longer behaves as a flag (i.e a parameter without an argument, assumed to imply truth), it now requires a value of either "true" or "false" (e.g. "--DoSomething false" or "--DoSomething true"). 

I can see arguments for both ways of doing it, although currently the main advantage of using a non-nullable bool using flag behavior is one of brevity, and familiarity to shell programmers; that's it. From the C# development side of things, being saddled with the restriction of making all boolean command line arguments be false for the sake of making the user experience of a subset of users a tiny bit easier isn't worth the constraints. At least in my cases, most of the applications I'm building are processes which are going to be invoked by other process (such as a SQL agent job calling a powershell script to do some prep for data to ingest). In those cases, it's better to just be explicit about what the parameter is supposed to be doing, and also allowing the C# code to behave however is natural to its flow.

Regardless, my point was less to state emphatically that one way is clearly superior to the other (at best, my feelings on the matter are lukewarm), but I did run into this problem while trying to make boolean parameters which essentially default to true. In summary, you can do this if you make the data type bool? instead of bool. Everything else is just discussing what happens when you do.

Enjoy.

P.s. here's a little C# console app you can use to try see what different values and data types and defaults will yield in different circumstances. Just make sure you first run Install-Package CommandLineParser
using CommandLine;
using static System.Console;


string[] rgs = args.Any()
    ? args
    : new string[]
    {
        //"--DefaultFalse", "true",
        //"--DefaultTrue", "false"
    };



Parser.Default.ParseArguments<Options>(rgs)
    .WithParsed(a =>
    {
        WriteLine("DefaultFalse: {0}", a.DefaultFalse.);
        WriteLine("DefaultTrue: {0}", a.DefaultTrue);
    });

class Options
{
    [Option("DefaultFalse", Required = false, Default = false)]
    public bool? DefaultFalse { get; set; }

    [Option("DefaultTrue", Required = false, Default = true)]
    public bool? DefaultTrue { get; set; }
}

Comments

Popular posts from this blog

Migrate SQLPrompt Snippets to VSCode

Fixing Git "Unexpected Disconnect while reading sideband packet"

Left-Padding Zeroes