Skip to content

Instantly share code, notes, and snippets.

@sandrock
Last active April 27, 2024 08:18
Show Gist options
  • Save sandrock/d1fb3040e1c9326d8dd16b3bad8930ac to your computer and use it in GitHub Desktop.
Save sandrock/d1fb3040e1c9326d8dd16b3bad8930ac to your computer and use it in GitHub Desktop.
ParseArgs.cs

The ParseArgs class is to be used to parse CLI arguments in an enumerator fashion. Here is a copy-paste gist.

Sample usage:

string[] args;
string arg, type;
bool doTrace = false;
using (var parser = new ParseArgs(args))
{
    while (parser.MoveNext())
    {
        if (parser.Is("--help"))
        {
            ShowUsage();
            Environment.ExitCode = 0;
            return;
        }
        else if (parser.Is("--trace"))
        {
            doTrace = true;
        }
        else if (parser.Is("--no-trace"))
        {
            doTrace = false;
        }
        else if (parser.Is(arg = "-t"))
        {
            if (parser.Remains(1))
            {
                parser.MoveNext();
                if (type != null)
                {
                    errors.Add("Type is already defined");
                }
                else
                {
                    type = parser.Current;
                }
            }
            else
            {
                errors.Add("Argument " + arg + " requires a value");
            }
        }
        else
        {
            errors.Add("Unknown argument " + parser.Current);
        }
    }
}
namespace Somewhere
{
using System;
using System.Collections;
using System.Collections.Generic;
// from <https://gist.github.com/sandrock/d1fb3040e1c9326d8dd16b3bad8930ac>
/// <summary>
/// Helps parse CLI arguments in a loop.
/// </summary>
public sealed class ParseArgs : IEnumerator<string>
{
private readonly String[] args;
private readonly StringComparison defaultStringComparison;
private int index = -1;
/// <summary>
/// Helps parse CLI arguments in a loop.
/// </summary>
public ParseArgs(StringComparison defaultStringComparison, string[] args)
{
if (args == null)
{
throw new ArgumentNullException(nameof(args));
}
this.defaultStringComparison = defaultStringComparison;
this.args = args.ToArray();
this.index = -1;
}
/// <summary>
/// Helps parse CLI arguments in a loop.
/// </summary>
public ParseArgs(string[] args)
: this(StringComparison.OrdinalIgnoreCase, args)
{
}
object IEnumerator.Current => this.Current;
/// <summary>
/// Gets the current argument.
/// </summary>
public String Current
{
get { return this.args[this.index]; }
}
/// <summary>
/// Gets the current index.
/// </summary>
public int Index
{
get { return this.index; }
}
/// <summary>
/// Checks whether the current argument is one of the specified values.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public bool Is(params string[] value)
{
return this.Is(this.defaultStringComparison, value);
}
/// <summary>
/// Checks whether the current argument is one of the specified values.
/// </summary>
/// <param name="stringComparison"></param>
/// <param name="value"></param>
/// <returns></returns>
public bool Is(StringComparison stringComparison, params string[] value)
{
foreach (var value0 in value)
{
if (this.Current.Equals(value0, stringComparison))
{
return true;
}
}
return false;
}
/// <summary>
/// Increments the current index, moving to the next argument.
/// </summary>
/// <returns></returns>
public bool MoveNext()
{
this.index++;
return this.index < this.args.Length;
}
public void Reset()
{
this.index = -1;
}
/// <summary>
/// Checks whether a quantity of arguments if available after the current one.
/// </summary>
/// <param name="count"></param>
/// <returns></returns>
public bool Has(int count)
{
return this.args.Length - this.index - count > 0;
}
/// <summary>
/// Gets the remaining items, without moving.
/// </summary>
/// <returns></returns>
public string[] GetNexts()
{
var result = new string[this.args.Length - this.index];
Array.Copy(this.args, this.index, result, 0, result.Length);
return result;
}
/// <summary>
/// Gets the remaining items, moving to the end.
/// </summary>
/// <returns></returns>
public string[] Remains()
{
this.MoveNext();
var result = new string[this.args.Length - this.index];
for (int i = 0; i < result.Length; i++)
{
result[i] = this.Current;
this.MoveNext();
}
return result;
}
public void Dispose()
{
}
}
}
namespace Somewhere
{
using System;
using Xunit;
// from <https://gist.github.com/sandrock/d1fb3040e1c9326d8dd16b3bad8930ac>
public class ParseArgsTests
{
[Fact]
public void LoopOnZero()
{
var args = Array.Empty<string>();
var target = new ParseArgs(args);
Verify(target, args);
}
[Fact]
public void LoopOnOne()
{
var args = new string[] { "a", };
var target = new ParseArgs(args);
Verify(target, args);
}
[Fact]
public void LoopOnTwo()
{
var args = new string[] { "a", "b", };
var target = new ParseArgs(args);
Verify(target, args);
}
[Fact]
public void RemainsOnZero()
{
var args = Array.Empty<string>();
var target = new ParseArgs(args);
var result = target.Remains();
Assert.Empty(result);
Assert.Equal(0, target.Index);
}
[Fact]
public void RemainsOnOne()
{
var args = new string[] { "a", };
var target = new ParseArgs(args);
var result = target.Remains();
Assert.Collection(
result,
x => { Assert.Equal(args[0], x); });
Assert.Equal(1, target.Index);
}
[Fact]
public void RemainsOnTwo()
{
var args = new string[] { "a", "b", };
var target = new ParseArgs(args);
var result = target.Remains();
Assert.Collection(
result,
x => { Assert.Equal(args[0], x); },
x => { Assert.Equal(args[1], x); });
Assert.Equal(2, target.Index);
}
private static void Verify(ParseArgs target, string[] args)
{
var itemCount = 0;
while (target.MoveNext())
{
Assert.False(target.Has(args.Length - itemCount));
Assert.Equal(itemCount, target.Index);
Assert.Equal(args[itemCount], target.Current);
Assert.True(target.Is(args[itemCount]));
Assert.True(target.Is(args[itemCount], "xxx"));
var nexts = target.GetNexts();
Assert.Equal(args.Length - itemCount, nexts.Length);
itemCount++;
}
Assert.Equal(args.Length, itemCount);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment