February 20, 2017

Comand Line Parser Class

A special command line parser class that automatically parses command lines specified in a particular way
    // Parse command line arguments in the form
    // /xxx
    // -xxx
    // OR
    // /xxx:yyy
    // -xxx:yyy
    // where xxx is a parameter name and yyy, when present, is a value to associate with it
    // The actual command line argument is case insensitive (but not the value)
    // so /username:Bob is the same as /USERNAME:Bob
    public class CommandLineParser
    {
        readonly Dictionary<string, string> arguments = new Dictionary<string, string>();

        public void ParseCommandLineArguments(
            params string[] args )
        {
            const int NOTFOUND = -1;
            //string[] args = Environment.GetCommandLineArgs();
            Debug.WriteLine( "Args:" + string.Join( ",", args ) );
            int ix = 0;
            foreach ( string arg in args )
            {
                Debug.WriteLine( "arg[" + ix++.ToString() + "]=\'" + arg + "\'" );
            }
            foreach ( string rawArg in args )
            {
                // Get rid of whitespace chars at the beginning and end
                string arg = rawArg.Trim();
                bool argFound = ( arg.Length >= 2 ) && ( arg[ 0 ] == '/' || arg[ 0 ] == '-' );
                if ( !argFound )
                    continue;
                string argument = arg.Substring( 1 );
                int end = argument.IndexOf( ':' );
                string paramName = ( end > -1 ) ? argument.Substring( 0, end ) : argument;
                if ( ( end == NOTFOUND ) && ( paramName.Length > 0 ) )
                {
                    arguments.Add( paramName.ToUpperInvariant(), "" );
                }
                else
                {
                    string value = argument.Substring( end + 1 );
                    arguments.Add( paramName.ToUpperInvariant(), value );
                }
            }
            Debug.WriteLine( "Processed command line args:" );
            foreach ( string argName in arguments.Keys )
            {
                Debug.WriteLine( argName + " = " + arguments[ argName ] );
            }
        }

        public bool GetArgument(
            string arg,
            out string value )
        {
            value = "";
            return arguments.TryGetValue( arg.ToUpperInvariant(), out value );
        }

        // eg '/u' could select the argument /username:Jim as long as no other arguments starts with 'u'
        public bool GetUniqueArgumentStartingWith(
            string startsWith,
            out string value )
        {
            value = "";
            bool res = string.IsNullOrEmpty( startsWith.Trim() );

            if ( !res )
            {
                var keys = arguments.Keys.Where( ky => ky.StartsWith( startsWith.ToUpperInvariant() ) ).ToList();
                res = ( keys.Count == 1 );
                if ( res )
                {
                    res = arguments.TryGetValue( keys[ 0 ], out value );
                }
            }
            return res;
        }

        public bool HasUniqueArgumentStartingWith(
            string startsWith )
        {
            bool res = !string.IsNullOrEmpty( startsWith.Trim() );

            if ( res )
            {
                var keys = arguments.Keys.Where( ky => ky.StartsWith( startsWith.ToUpperInvariant() ) );
                res = ( keys.Count() == 1 );
            }
            return res;
        }

        public bool HasArgument(
            string arg )
        {
            return arguments.ContainsKey( arg.ToUpperInvariant() );
        }

        public IEnumerable<KeyValuePair<string, string>> Arguments()
        {
            foreach ( var entry in arguments )
            {
                yield return entry;
            }
        }
    }
Unit testss that accomapny and demonstrate the use of the class:
[ TestFixture ]
public class CommandLineParserUnitTests
{
    [ Test ]
    public void SeveralValidArgsTest()
    {
        CommandLineParser clp = new CommandLineParser();
        clp.ParseCommandLineArguments( "/user:John", "-password:All0aJadgar", "-IncludeMetaData" );

        Assert.That( clp.HasArgument( "IncludeMetaData" ) );
        string value;
        bool res = clp.GetArgument( "user", out value );
        Assert.IsTrue( res );
        Assert.That( value == "John" );
        res = clp.GetArgument( "smugering", out value );
        Assert.IsFalse( res );
    }

    [ Test ]
    public void EmptyArgAndValueTest()
    {
        CommandLineParser clp = new CommandLineParser();
        clp.ParseCommandLineArguments( "/:" );

        Assert.That( !clp.HasArgument( ":" ) );
    }

    [ Test ]
    public void NoValueSpecifiedTest()
    {
        CommandLineParser clp = new CommandLineParser();
        clp.ParseCommandLineArguments( "/dufftest:" );
        string value;

        Assert.That( clp.GetArgument( "dufftest", out value ) );
        Assert.That( value.Length == 0 );
        Assert.That( clp.HasArgument( "dufftest" ) );
    }
}

No comments: